Обработка нескольких строк подключения в основном веб-интерфейсе asp.net, который является параметром - PullRequest
0 голосов
/ 03 декабря 2018

В одном моем приложении я столкнулся с ситуацией, когда пользователь должен выбрать базу данных, которую необходимо использовать из внешнего интерфейса.

И затем в каждом запросе выбранное значение передается в качестве параметра каквсе это 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.

Здесь также у меня есть constructor с DI.

Мой вопрос: есть ли какой-нибудь другой лучший способ сделать это без writing/calling в каждом action.Я считаю, что должно быть, но я не могу пройти через это.

То, что я ищу, как: -

  • Любой способ получить это путем инъекции через DI.
  • Любой способ инициализации с помощью constructor.
  • Любой способ с помощью action filters.
  • Или любой лучший подход.

1 Ответ

0 голосов
/ 04 декабря 2018

Я рекомендую создать класс DbContextProvider, у которого есть метод, который создает строку подключения для имени базы данных.Затем зарегистрируйте его в DI и используйте фильтр действий для заполнения свойства контроллера.

Таким образом логика выбора БД отделена от запроса и может использоваться (и тестироваться) вне веб-приложения.,Также у вас есть возможность запретить вызывать действие, если указано недопустимое имя БД (в фильтре действий).

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

Это также позволяет использовать одного и того же поставщика в фоновых заданиях или в случаях, когда вам требуется доступ к нескольким базам данных.Однако вы больше не можете напрямую внедрять DbContext с помощью DI.

Если вам требуется выполнить миграцию, вам, возможно, придется также изучить создание DbContext во время разработки: https://docs.microsoft.com/en-us/ef/core/miscellaneous/cli/dbcontext-creation

...