这里有一个示例,展示了如何实现一个
DbContextFactory
或
DbContextProxy<T>
,它将创建正确的提供程序并返回它。
public interface IDbContextFactory
{
ApplicationContext Create();
}
public class DbContextFactory() : IDbContextFactory, IDisposable
{
private ApplicationContext context;
private bool disposing;
public DbContextFactory()
{
}
public ApplicationContext Create()
{
if(this.context==null)
{
string providerType = ...;
string connectionString = ...;
var dbContextBuilder = new DbContextOptionsBuilder();
if(providerType == "MSSQL")
{
dbContextBuilder.UseSqlServer(connectionString);
}
else if(providerType == "Sqlite")
{
dbContextBuilder.UseSqlite(connectionString);
}
else
{
throw new InvalidOperationException("Invalid providerType");
}
this.context = new ApplicationContext(dbContextBuilder);
}
return this.context;
}
public void Dispose(){
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing){
if (disposing){
disposing?.Dispose();
}
}
}
同时,确保您按照上面所示实现可处理模式,以便在工厂被处理时上下文也被处理,以防止DbContext在内存中停留的时间超过必要的时间,并尽快释放非托管资源。
最后将工厂注册为范围作用域,就像注册上下文本身一样:
services.AddScopedd<IDbContextFactory, DbContextFactory>()
一个更高级和通用/可扩展的方法是创建一个
IDbContextProxy<T>
类,它使用一些反射来获取正确的构造函数和
DbContextOptionsBuilder
。
还可以创建一个抽象提供程序创建的
IDbContextBuilder
。
public class SqlServerDbContextBuilder IDbContextBuilder
{
public bool CanHandle(string providerType) => providerType == "SqlServer";
public T CreateDbContext<T>(connectionString)
{
T context = ...
return context;
}
}
然后您可以通过以下方式选择正确的提供程序,而无需使用硬编码的 if/else
或 switch
块:
var providerType = "SqlServer";
var builder = builders.Where(builder => builder.CanHandle(providerType)).First();
var context = builder.CreateDbContext<ApplicationContext>(connectionString);
添加新的提供程序就像添加依赖项和一个 XxxDbContextBuilder
类一样简单。
有关此类方法的更多信息,请参见 此处, 此处 或 此处。
AddDbContext
并通过抽象工厂解决您的DB上下文,后者读取配置值并手动选择正确的提供程序进行构造,但是这样您将需要自己管理其生命周期(即处理其释放)。 - Tseng