【问题标题】:Handling multiple connection strings in asp.net core web api which came as a parameter在 asp.net core web api 中处理多个连接字符串作为参数
【发布时间】:2019-05-05 02:13:34
【问题描述】:

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

然后在每个请求中,选择的值作为参数传递,因为所有的都是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,它也会为我做同样的事情,但我确实需要每次都为所有actions调用method

这里我还有一个constructorDI

我的问题是,在每个 action 中没有 writing/calling 的情况下,还有其他更好的方法吗?我认为应该有,但我无法通过它。

我在寻找什么:-

  • 通过DI 注入任何方式来实现此功能。
  • 通过constructor进行初始化的任何方式。
  • action filters.
  • 或任何更好的方法。

【问题讨论】:

  • 我选择的过滤器 - 这些是处理这种关闭方法的最佳选择

标签: c# asp.net-core dependency-injection custom-action-filter


【解决方案1】:

我建议创建一个 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。 services.AddScoped&lt;AppDbContextProvider&gt;();

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-05-23
    • 1970-01-01
    • 2021-09-20
    • 1970-01-01
    • 1970-01-01
    • 2018-05-29
    • 2015-09-14
    相关资源
    最近更新 更多