Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use AddDbContextPool if all configuration in OnConfiguring method of DbContext

I'm using PostgreSQL and I have ApplicationDbContext like:

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);
            });
        }
    }
}

That context is base for many others. I'm on a way of improvement performance and trying to use context pooling . Docs say to add polling I should:

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

But I want to store.UseNpgsql and other configs of DbContext in OnConfiguring method. How to achieve that?

like image 937
Pr.Dumbledor Avatar asked Apr 23 '19 20:04

Pr.Dumbledor


People also ask

What is AddDbContextPool?

AddDbContextPool<TContext>(IServiceCollection, Action<DbContextOptionsBuilder>, Int32) Registers the given DbContext as a service in the IServiceCollection, and enables DbContext pooling for this registration. DbContext pooling can increase performance in high-throughput scenarios by re-using context instances.

Which method is used for adding DbContext class as service?

The AddDbContext extension method registers DbContext types with a scoped lifetime by default.

When registering multiple DbContext types make sure that the constructor for each context type has?

When registering multiple DbContext types make sure that the constructor for each context type has a DbContextOptions parameter rather than a non-generic DbContextOptions parameter.


Video Answer


1 Answers

Besides the debatable benefits of using it (from the docs: "has the advantage of saving some of the cost of initialization of DbContext instance"), the DbContext pooling is simply not applicable in your scenario, because your context contains state which EF Core is unaware of:

private readonly DatabaseSettings _databaseOptions;

and the Limitations section of the documentation clearly states:

Warning!

Avoid using DbContext Pooling if you maintain your own state (for example, private fields) in your derived DbContext class that should not be shared across requests. EF Core will only reset the state that is aware of before adding a DbContext instance to the pool.


There is a reason why optionsAction of AddDbContextPool is required, while for AddDbContext it is optional. It's because of the aforementioned limitation, plus the additional requirement that your DbContext derived class must have single public constructor with single DbContextOptions parameter. You can easily see that by fooling the AddDbContextPool with passing empty action:

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

but then at runtime you'll get InvalidOperationException saying

The DbContext of type 'ApplicationDbContext' cannot be pooled because it does not have a single public constructor accepting a single parameter of type DbContextOptions.

So in order to be eligible for pooling, you have to remove all these

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

and add this instead

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

Now you should clearly see why what you are asking is not possible. Your OnConfiguring method requires DatabaseSettings, but there is no way you could provide it. Hence the options must be configured externally.

In other words, your requirements are mutually exclusive, hence there is no solution.

like image 72
Ivan Stoev Avatar answered Oct 24 '22 10:10

Ivan Stoev