0

在我的一个应用程序中,我遇到了用户必须从前端选择需要使用的数据库的情况。

然后在每个请求中,所选值作为参数传递,因为所有都是web api调用,并且需要根据所选数据库建立连接。

所以目前我正在编写代码来初始化每个action method. 喜欢 :-

public async Task<IActionResult> GetData([FromQuery]string Id, [FromQuery]string database)
{
    if(database=="A")
       connection conn=new connection("connStringA");//dummy code
    if(database=="B")
       connection conn=new connection("connStringB");//dummy code
    // and so on the logic......... 
}

我也可以创建一个单独的method,它会为我做同样的事情,但我确实需要method每次都为所有actions.

这里我也有一个constructorwith DI

我的问题是没有其他更好的方法可以做到这writing/calling一点action。我相信应该有,但我无法通过它。

我正在寻找的东西是:-

  • 任何方式都可以通过注入来实现DI
  • 任何初始化方式constructor
  • 无论如何action filters
  • 或者任何更好的方法。
4

1 回答 1

0

我建议创建一个 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);
    }
}

然后最后将AppDbContextProviderDI 作为范围服务添加到应用程序 DI。 services.AddScoped<AppDbContextProvider>();

这也允许在后台作业或您必须访问多个数据库的情况下使用相同的提供程序。但是,您不能再直接通过 DI 注入 DbContext。

如果您需要迁移,您可能还需要查看设计时 DbContext 创建: https ://docs.microsoft.com/en-us/ef/core/miscellaneous/cli/dbcontext-creation

于 2018-12-03T21:19:42.670 回答