我建议创建一个 DbContextProvider 类,该类具有为数据库名称创建连接字符串的方法。然后在 DI 中注册它,并使用操作过滤器来填充 Controller 的属性。
这样,数据库选择逻辑与请求分离,并且可以在 Web 应用程序之外使用(和测试)。如果提供了无效的数据库名称(在操作过滤器内),您还可以选择阻止调用该操作。
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{
}
}
public class AppDbContextProvider : IDisposable
{
//enforcing 1 DbContext per request
private Dictionary<string, AppDbContext> _contexts = new Dictionary<string, AppDbContext>();
public AppDbContext GetDbContext(string dbName)
{
if (dbName == null)
return null;
if (_contexts.TryGetValue(dbName, out AppDbContext ctx))
return ctx;
var conStr = GetConnectionString(dbName);
if (conStr == null)
return null;
var dbOptionsBuilder = new DbContextOptionsBuilder<AppDbContext>();
dbOptionsBuilder.UseSqlServer(conStr);
ctx = new AppDbContext(dbOptionsBuilder.Options);
_contexts[dbName] = ctx;
return ctx;
}
//Any connection string selection logic, either hard-coded or configurable somewhere (e.g. Options).
private string GetConnectionString(string dbName)
{
switch (dbName)
{
case "A":
return "a";
case "B":
return "b";
default:
return null;
}
}
//ensure clean dispose after DI scope lifetime
public void Dispose()
{
if (_contexts.Count > 0)
{
foreach (var ctx in _contexts.Values)
ctx.Dispose();
}
}
}
public class PopulateDbContextFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var dbName = filterContext.HttpContext.Request.Query["db"];
var provider = filterContext.HttpContext.RequestServices.GetRequiredService<AppDbContextProvider>();
var ctx= provider.GetDbContext(dbName);
if (ctx == null)
{
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new { controller = "Home", action = "Error" }));
}else
{
//could also be stored to any other accessible location (e.g. an controller property)
filterContext.HttpContext.Items["dbContext"] = ctx;
}
base.OnActionExecuting(filterContext);
}
}
然后最后将AppDbContextProvider
DI 作为范围服务添加到应用程序 DI。
services.AddScoped<AppDbContextProvider>();
这也允许在后台作业或您必须访问多个数据库的情况下使用相同的提供程序。但是,您不能再直接通过 DI 注入 DbContext。
如果您需要迁移,您可能还需要查看设计时 DbContext 创建:
https ://docs.microsoft.com/en-us/ef/core/miscellaneous/cli/dbcontext-creation