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



我正在使用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));

但我想存储。在OnConfiguration方法中使用Npgsql和DbContext的其他配置。如何实现?

除了使用它有争议的好处(来自文档:">具有节省一些 DbContext 实例初始化成本的优点")之外,DbContext池根本不适用于你的方案,因为你的上下文包含 EF Core 不知道的状态

private readonly DatabaseSettings _databaseOptions;

文档的"限制"部分明确指出:

警告!

如果在派生的 DbContext 类中维护自己的状态(例如,私有字段),而不应在请求之间共享,请避免使用 DbContext 池。EF Core 只会重置在将 DbContext 实例添加到池之前识别的状态。


需要optionsActionAddDbContextPool是有原因的,而对于AddDbContext它是可选的。这是因为上述限制,以及DbContext派生类必须具有具有单个DbContextOptions参数的单个公共构造函数的额外要求。您可以通过传递空操作来愚弄AddDbContextPool,从而轻松看到这一点:

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

但是在运行时你会得到InvalidOperationException

类型为"ApplicationDbContext"的 DbContext 不能池化,因为它没有单个公共构造函数接受 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

换句话说,您的要求是相互排斥的,因此没有解决方案。

作为@Ivan Stoev答案的附录,如果有人需要一种方法为选项实例创建可重用的逻辑,然后用于初始化PooledDbContext的配置,他们可以采取以下方法:

正如 Ivan 所澄清的那样,您的DbContext本身必须只有一个接受DbContextOptions<TContext>的构造函数。但是,没有什么可以阻止您在传递给AddDbContextPool()方法Action<DbContextOptions<TContext>>中创建自己的独立ConfigurationBuilder。例如。。。

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

如果您在使用池时确实需要按实例DbContext配置,则可以遵循此处的文档建议。

通过服务集合访问DbContext实例,并使用从池IDbContextFactory<TContext>实例化DbContext的注册工厂方法,然后可以对使用DbContext的代码执行任何类型的初始化交接。当与IOptionsSnapshot<T>IOptionsMonitor<T>结合使用以将实时配置数据应用于此初始化过程时,这尤其有用。

相关内容

  • 没有找到相关文章

最新更新