[英]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. 有一个原因需要
optionsAction
的AddDbContextPool
,而对于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.