简体   繁体   English

如果所有配置都在 DbContext 的 OnConfiguring 方法中,如何使用 AddDbContextPool

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

I'm using PostgreSQL and I have ApplicationDbContext like:我正在使用 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);
            });
        }
    }
}

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.但我想在 OnConfiguring 方法中存储.UseNpgsql 和 DbContext 的其他配置。 How to achieve that?如何实现?

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: 除了使用它的有争议的好处(来自文档: “具有节省DbContext实例初始化的一些成本的优势” ), DbContext池在您的场景中根本不适用,因为您的上下文包含EF Core 所属的 状态不知道:

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. 如果在派生的DbContext类中维护自己的状态(例如,私有字段),不应在请求之间共享,请避免使用DbContext池。 EF Core will only reset the state that is aware of before adding a DbContext instance to the pool. EF Core只会在将DbContext实例添加到池之前重置已知的状态。


There is a reason why optionsAction of AddDbContextPool is required, while for AddDbContext it is optional. 有一个原因需要optionsActionAddDbContextPool ,而对于AddDbContext它是可选的。 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. 这是因为上述限制,以及您的DbContext派生类必须具有单个 DbContextOptions参数的单个公共构造函数的附加要求。 You can easily see that by fooling the AddDbContextPool with passing empty action: 通过传递空操作来欺骗AddDbContextPool ,您可以很容易地看到:

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

but then at runtime you'll get InvalidOperationException saying 但是在运行时你会得到InvalidOperationException

The DbContext of type 'ApplicationDbContext' cannot be pooled because it does not have a single public constructor accepting a single parameter of type DbContextOptions. 无法合并类型为“ApplicationDbContext”的DbContext,因为它没有单个公共构造函数接受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. 您的OnConfiguring方法需要DatabaseSettings ,但您无法提供它。 Hence the options must be configured externally. 因此,必须在外部配置options

In other words, your requirements are mutually exclusive, hence there is no solution. 换句话说,您的要求是互斥的,因此没有解决方案。

As an addendum to @Ivan Stoev's answer , should anyone need a way of creating reusable logic for an options instance that is then used to initialize a PooledDbContext 's configuration, they can take the following approach:作为@Ivan Stoev's answer的附录,如果任何人都需要一种为选项实例创建可重用逻辑的方法,然后用于初始化PooledDbContext的配置,他们可以采用以下方法:

As Ivan clarified, your DbContext itself must have only a single constructor that takes in the DbContextOptions<TContext> .正如 Ivan 所阐明的,您的DbContext本身必须只有一个接受DbContextOptions<TContext>的构造函数。 However, there's nothing stopping you from creating your own independent ConfigurationBuilder in the Action<DbContextOptions<TContext>> you pass to the AddDbContextPool() method.但是,没有什么可以阻止您在传递给AddDbContextPool()方法的Action<DbContextOptions<TContext>>中创建自己的独立ConfigurationBuilder For example...例如...

public class Program
{
    static int Main(string[] args)
    {
        var host = Host.CreateDefaultBuilder(args)
            .ConfigureServices((host, services) => {
                // Build DatabaseSettings from config just like IOptions<T> would.
                var config = host.Configuration;
                var settings = new DatabaseSettings();
                config.Bind(nameof(DatabaseSettings), settings);

                // Use settings to configure pool.
                services.AddPooledDbContextFactory<EmployeeContext>(optionsBuilder => {
                    if (settings.ConnectionString == null)
                    {
                        optionsBuilder.UseInMemoryDatabase(Guid.NewGuid().ToString());
                    }
                    else
                    {
                        optionsBuilder.UseNpgsql(settings.ConnectionString,
                        npgsqlOptionsAction: sqlOptions =>
                        {
                            sqlOptions.EnableRetryOnFailure(
                                maxRetryCount: settings.MaxRetryCount,
                                maxRetryDelay: TimeSpan.FromSeconds(settings.MaxRetryDelay),
                                errorCodesToAdd: null);
                        });
                    }
                });
                
                // Add example scoped configuration options
                services.AddOptions<MyOptions>();

                // Initialize each DbContext upon request
                services.AddScoped<EmployeeContext>(srv => {
                    var factory = srv.GetRequiredService<IDbContextFactory<EmployeeContext>>();
                    var snapshot = srv.GetRequiredService<IOptionsSnapshot<MyOptions>>();
                    var ctx = factory.CreateDbContext();

                    // ctx.SomeProperty = snapshot.Value.SomeProperty;
                    return ctx;
                });
            }).Build();
    }
}

If you really do need per-instance DbContext configuration when using a pool, then you can follow the documentation's recommendation here .如果在使用池时确实需要每个实例的DbContext配置,那么您可以按照此处文档的建议进行操作。

By accessing the DbContext instance through the service collection and using a registered factory method that instantiates the DbContext from the pooled IDbContextFactory<TContext> , you can then do any sort of initialization hand-off you need for code that uses your DbContext .通过服务集合访问DbContext实例并使用从池化IDbContextFactory<TContext>实例化DbContext的注册工厂方法,然后您可以对使用DbContext的代码执行任何类型的初始化切换。 This is especially useful when combined with IOptionsSnapshot<T> or IOptionsMonitor<T> to apply real-time configuration data to this initialization process.这在与IOptionsSnapshot<T>IOptionsMonitor<T>结合使用以将实时配置数据应用于此初始化过程时特别有用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么我们在 DbContext 中使用 DependecyInjection 而不是 OnConfiguring 方法? - Why do we use DependecyInjection instead of OnConfiguring Method in the DbContext? 如何在 dbContext OnConfiguring 生成中插入自定义代码? - How to insert custom codes in dbContext OnConfiguring generation? 如何在没有任何硬编码连接字符串的情况下在我的 DbContext 中设置 OnConfiguring 方法? - How do I set the OnConfiguring method in my DbContext without any hardcoded connection string? 可以通过覆盖DbContext.OnConfiguring来配置提供程序 - A provider can be configured by overriding the DbContext.OnConfiguring 何时在 DbContext 构造函数与 OnConfiguring 中提供 DbContextOptions? - when to provide DbContextOptions in DbContext constructor vs OnConfiguring? 我应该使用 AddDbContext 还是 AddDbContextPool - Should I use AddDbContext or AddDbContextPool 找不到合适的方法来覆盖 - OnConfiguring - No Suitable Method Found To Override - OnConfiguring OnConfiguring方法上的System.TypeLoadException - System.TypeLoadException at OnConfiguring method 如何使用 Moq 测试创建新 DbContext 的方法? - How can I use Moq to test a method that creates a new DbContext? 尝试使用 onconfiguring dbcontext 选项激活时无法解析服务类型 - Unable to resolve service for type while attempting to activate with onconfiguring dbcontext options
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM