Как использовать AddDbContextPool, если вся конфигурация в методе OnConfiguring DbContext

Я использую PostgreSQL, и у меня есть ApplicationDbContext, например:

public class ApplicationDbContext : DbContext
{
    private readonly DatabaseSettings _databaseOptions;
    public ApplicationDbContext() { }
    public ApplicationDbContext(IOptions<DatabaseSettings> databaseOptions)
    {            
        _databaseOptions = databaseOptions.Value;
    }
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.HasPostgresExtension("citext");
    }
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        if (_databaseOptions == null)
        {
            optionsBuilder.UseInMemoryDatabase(Guid.NewGuid().ToString());
        }
        else
        {
            optionsBuilder.UseNpgsql(_databaseOptions.ConnectionString,
            npgsqlOptionsAction: sqlOptions =>
            {
                sqlOptions.EnableRetryOnFailure(
                    maxRetryCount: _databaseOptions.MaxRetryCount,
                    maxRetryDelay: TimeSpan.FromSeconds(_databaseOptions.MaxRetryDelay),
                    errorCodesToAdd: null);
            });
        }
    }
}

Этот контекст является основой для многих других. Я нахожусь на пути улучшения производительности и пытаюсь использовать объединение контекстов. Документы говорят, что для добавления опроса я должен:

services.AddDbContextPool<EmployeeContext>(options => options.UseNpgsql(connection));

Но я хочу хранить.UseNpgsql и другие конфиги DbContext в методе OnConfiguring. Как этого добиться?


person Pr.Dumbledor    schedule 23.04.2019    source источник
comment
какое приложение у вас запущено? Это приложение ASP.NET?   -  person jpgrassi    schedule 29.04.2019
comment
да. asp.net webapi   -  person Pr.Dumbledor    schedule 03.05.2019


Ответы (1)


Помимо спорных преимуществ его использования (из документации: "имеет то преимущество, что экономит часть стоимости инициализации экземпляра DbContext"), DbContext pooling просто неприменим в вашем сценарии, потому что ваш контекст содержит состояние, о котором EF Core не знает:

private readonly DatabaseSettings _databaseOptions;

и Ограничения в разделе документации четко указано:

Внимание!

Избегайте использования пула DbContext, если вы поддерживаете собственное состояние (например, закрытые поля) в своем производном классе DbContext, которое не должно использоваться совместно между запросами. EF Core будет сбрасывать только то состояние, о котором известно перед добавлением экземпляра DbContext в пул.


Есть причина, по которой optionsAction из AddDbContextPool требуется, а для AddDbContext это необязательно. Это связано с вышеупомянутым ограничением, а также с дополнительным требованием, чтобы ваш DbContext производный класс имел один общедоступный конструктор с одиночным параметром DbContextOptions. Вы можете легко убедиться в этом, обманув AddDbContextPool передачей пустого действия:

services.AddDbContextPool<ApplicationDbContext>(options => { });

но тогда во время выполнения вы получите InvalidOperationException сообщение

DbContext типа ApplicationDbContext не может быть объединен в пул, так как у него нет единого общедоступного конструктора, принимающего один параметр типа DbContextOptions.

Таким образом, чтобы иметь право на объединение, вы должны удалить все эти

private readonly DatabaseSettings _databaseOptions;
public ApplicationDbContext() { }
public ApplicationDbContext(IOptions<DatabaseSettings> databaseOptions)
{            
    _databaseOptions = databaseOptions.Value;
}

и добавьте это вместо

public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { }

Теперь вы должны ясно видеть, почему то, о чем вы просите, невозможно. Для вашего метода OnConfiguring требуется DatabaseSettings, но вы никак не можете его предоставить. Следовательно, options должен быть сконфигурирован извне.

Другими словами, ваши требования взаимоисключающие, поэтому решения нет.

person Ivan Stoev    schedule 30.04.2019
comment
Как я могу это изменить? - person Pr.Dumbledor; 03.05.2019
comment
Как я уже упоминал, такой контекст, как ваш, не подходит для объединения контекстов. Другими словами, просто используйте AddDbContext. - person Ivan Stoev; 03.05.2019
comment
Вы не можете, если вам нужны эти DatabaseSettings. Пул контекста требует, чтобы контекст БД имел единственный общедоступный конструктор с параметром DbContextOptions (или DbContextOptions<TContext>). Таким образом, вы должны удалить параметр DatabaseSettings, но тогда вы не будете знать, как настроить свой контекст внутри OnConfiguring, поэтому его нужно настроить извне. Как видите, проблема не в вызове AddDbContextPool (его запросто можно вызвать с пустым действием), а в совокупности ваших требований, не имеющей решения. - person Ivan Stoev; 03.05.2019
comment
Я бы не сказал, что у этого нет решения - на самом деле ничего. @Pr.Dumbledor, в то время как @Ivan Stoev прав во всем, я бы сказал, что это зависит от того, можете ли вы скомпрометировать и установить параметры DatabaseSettings извне, где и когда вы создаете контекст БД, вы просто проверяете, не используются ли параметры DatabaseSettings не существуют (передаются конструктору) и создают память вместо реального соединения Psql db? Всегда ли MaxRetryCount и MaxRetryDelay одинаковы? - person eja; 04.05.2019
comment
@eja Но они содержат самую важную информацию - строку подключения. Без этого в OnConfiguring в принципе ничего нельзя сделать. И это именно то, что OP хочет в конце сообщения - Но я хочу сохранить.UseNpgsql и другие конфигурации DbContext в методе OnConfiguring. Как этого добиться? Мне кажется, это не решение :) - person Ivan Stoev; 04.05.2019
comment
@IvanStoev Понятно .. В любом случае комментарии выше, как я могу это изменить и как изменить его для объединения контекстов, просто заставили меня задуматься. Я согласен, единственный способ использовать пул контекстов - это если OP может переместить переопределение DatabaseSettings и OnConfiguring из ApplicationDbContext. Я не уверен, что я что-то упустил, не может ли он проверить строку подключения и создать ли контекст в памяти или Psql внутри/в services.AddDbContextPool()? И да, только если удаление OnConfiguring приемлемо..? @Пр.Дамблдор - person eja; 04.05.2019