ASP.NET Core изменить строку подключения EF при входе пользователя в систему

после нескольких часов исследований и не найдя способ сделать это, пришло время задать вопрос.

у меня есть ASP.NET проект Core 1.1 с использованием EF Core и MVC, который используется несколькими клиентами. Каждый клиент имеет свою собственную базу данных с точно такой же схемой. В настоящее время этот проект является приложением Windows, которое переносится в интернет. На экране входа в систему пользователь имеет три поля: код компании, имя пользователя и пароль. Мне нужно иметь возможность изменить строку соединения, когда пользователь пытается войти в систему на основе того, что они вводят в код компании, а затем запоминают их ввод на протяжении всего сеанса.

я нашел несколько способов сделать это с одной базой данных и несколькими схемами, но ни с несколькими базами данных, использующими одну и ту же схему.

я решил эту проблему не решить проблему, а обойти, что работал для меня. Мои базы данных и приложение размещены в Azure. Мое исправление к этому было обновить мое приложение обслуживание плана, который поддерживает слоты (только дополнительные $20 в месяц для 5 слотов). Каждый слот имеет одну и ту же программу, но переменная среды, содержащая строку подключения, специфична для компании. Таким образом, я также могу подчинить доступ каждой компании, если захочу. Хотя этот подход не может быть тем, что сделали бы другие, он был наиболее экономически эффективным для меня. Легче публиковать в каждом слоте, чем тратить часы на другое программирование, которое работает неправильно. Пока Microsoft не упростит изменение строки подключения это мое решение.

в ответ на ответ

кажется, это может сработать. Я попытался его реализовать. Одна вещь, которую я делаю, - это использование класса репозитория, который обращается к моему контексту. Мои контроллеры получают репозиторий, введенный в них, чтобы вызвать методы в репозитории, которые обращаются к контексту. Как это сделать в классе репозитория. Нет перегрузки OnActionExecuting в моем хранилище. Кроме того, если это сохраняется для сеанса, что происходит, когда пользователь снова открывает свой браузер для приложения и все еще входит в систему с файлом cookie, который длится 7 дней? Разве это не новый сеанс? Похоже, приложение выдаст исключение, потому что переменная сеанса будет равна null и поэтому не будет иметь полной строки подключения. Думаю, я также могу сохранить его как утверждение и использовать утверждение, если переменная сеанса равна null.

вот мой класс репозитория. IDbContextService был ProgramContext, но я начал добавлять ваши предложения, чтобы попытаться заставить его работать.

public class ProjectRepository : IProjectRepository
{
    private IDbContextService _context;
    private ILogger<ProjectRepository> _logger;
    private UserManager<ApplicationUser> _userManager;

    public ProjectRepository(IDbContextService context,
                            ILogger<ProjectRepository> logger,
                            UserManager<ApplicationUser> userManger)
    {
        _context = context;
        _logger = logger;
        _userManager = userManger;
    }

    public async Task<bool> SaveChangesAsync()
    {
        return (await _context.SaveChangesAsync()) > 0;
    }
}

в ответ на ответ силы JB

я попытался реализовать ваш подход. Я получаю исключение в программе.cs on line

host.Run();

вот моя программа.класс cs. Нетронутый.

using System.IO;
using Microsoft.AspNetCore.Hosting;

namespace Project
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var host = new WebHostBuilder()
                .UseKestrel()
                .UseContentRoot(Directory.GetCurrentDirectory())
                .UseIISIntegration()
                .UseStartup<Startup>()
                .Build();

            host.Run();
        }
    }
}

и мой " запуск.класс cs.

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using Project.Entities;
using Project.Services;

namespace Project
{
    public class Startup
    {
        private IConfigurationRoot _config;

        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appsettings.json")
                .AddEnvironmentVariables();

            _config = builder.Build();
        }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddSingleton(_config);
            services.AddIdentity<ApplicationUser, IdentityRole>(config =>
            {
                config.User.RequireUniqueEmail = true;
                config.Password.RequireDigit = true;
                config.Password.RequireLowercase = true;
                config.Password.RequireUppercase = true;
                config.Password.RequireNonAlphanumeric = false;
                config.Password.RequiredLength = 8;
                config.Cookies.ApplicationCookie.LoginPath = "/Auth/Login";
                config.Cookies.ApplicationCookie.ExpireTimeSpan = new TimeSpan(7, 0, 0, 0); // Cookies last 7 days
            })
            .AddEntityFrameworkStores<ProjectContext>();
            services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, AppClaimsPrincipalFactory>();
            services.AddScoped<IProjectRepository, ProjectRepository>();
            services.AddTransient<MiscService>();
            services.AddLogging();
            services.AddMvc()
            .AddJsonOptions(config =>
            {
                config.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
            });
        }

        public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
        {
            Dictionary<string, string> connStrs = new Dictionary<string, string>();
            connStrs.Add("company1", "1stconnectionstring"));
            connStrs.Add("company2", "2ndconnectionstring";
            DbContextFactory.SetDConnectionString(connStrs);
            //app.UseDefaultFiles();

            app.UseStaticFiles();
            app.UseIdentity();
            app.UseMvc(config =>
            {
                config.MapRoute(
                    name: "Default",
                    template: "{controller}/{action}/{id?}",
                    defaults: new { controller = "Auth", action = "Login" }
                    );
            });
        }
    }
}

и исключения:

InvalidOperationException: Unable to resolve service for type 'Project.Entities.ProjectContext' while attempting to activate 'Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore`4[Project.Entities.ApplicationUser,Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRole,Project.Entities.ProjectContext,System.String]'.

не уверен, что делать здесь.

частичный успех edit

хорошо, я получил ваш пример работы. Я могу установить строку подключения в моем конструкторе репозитория, используя другой идентификатор. Теперь моя проблема-войти в систему и выбрать правильную базу данных. Я подумал о том, что репозиторий вытащит из сеанса или утверждения, что бы ни было null. Но я не могу установить значение перед использованием SignInManager в контроллере входа в систему, потому что SignInManager вводится в контроллер, который создает контекст перед обновлением переменной сеанса. Единственный способ, который я могу придумать, - это иметь двухстраничный логин. На первой странице будет запрошен код компании и обновлена переменная сеанса. На второй странице будет использоваться SignInManager и репозиторий будет введен в конструктор контроллеров. Это произойдет после того, как первая страница обновит переменную сеанса. Это может быть более визуально привлекательным с анимацией между обоими представлениями входа. Если только у кого-нибудь нет идей, как сделайте это без двух представлений входа я попытаюсь реализовать двухстраничный вход и опубликовать код, если он работает.

он на самом деле сломан

когда он работал, это потому, что у меня все еще был действительный cookie. Я бы запустил проект, и он пропустил бы логин. Теперь я получаю исключение InvalidOperationException: No database provider has been configured for this DbContext после очистки кэша. Я прошел через все это, и контекст создается правильно. Я предполагаю, что у идентичности есть какие-то проблемы. Может ли приведенный ниже код добавить хранилища Entity framework в ConfigureServices быть причиной проблемы?

services.AddIdentity<ApplicationUser, IdentityRole>(config =>
{
    config.User.RequireUniqueEmail = true;
    config.Password.RequireDigit = true;
    config.Password.RequireLowercase = true;
    config.Password.RequireUppercase = true;
    config.Password.RequireNonAlphanumeric = false;
    config.Password.RequiredLength = 8;
    config.Cookies.ApplicationCookie.LoginPath = "/Company/Login";
    config.Cookies.ApplicationCookie.ExpireTimeSpan = new TimeSpan(7, 0, 0, 0); // Cookies last 7 days
})
.AddEntityFrameworkStores<ProgramContext>();

редактировать

Я проверил Identity проблема. Я вытащил данные из своего репозитория перед выполнением PasswordSignInAsync и он вытащил данные просто отлично. Как создается DbContext для идентификации?

5 ответов


создать фабрику DbContext

public static class DbContextFactory
{
    public static Dictionary<string, string> ConnectionStrings { get; set; }

    public static void SetConnectionString(Dictionary<string, string> connStrs)
    {
        ConnectionStrings = connStrs;
    }

    public static MyDbContext Create(string connid)
    {
        if (!string.IsNullOrEmpty(connid))
        {
            var connStr = ConnectionStrings[connid];
            var optionsBuilder = new DbContextOptionsBuilder<MyDbContext>();
            optionsBuilder.UseSqlServer(connStr);
            return new MyDbContext(optionsBuilder.Options);
        }
        else
        {
            throw new ArgumentNullException("ConnectionId");
        }
    }
}

Intialize DbContext factory

в автозагрузку.cs

public void Configure()
{
  Dictionary<string, string> connStrs = new Dictionary<string, string>();
  connStrs.Add("DB1", Configuration["Data:DB1Connection:ConnectionString"]);
  connStrs.Add("DB2", Configuration["Data:DB2Connection:ConnectionString"]);
  DbContextFactory.SetConnectionString(connStrs);
}

использование

var dbContext= DbContextFactory.Create("DB1");

согласно вашему вопросу, я собираюсь предоставить решение, предполагающее некоторые вещи:

во-первых, я создал три базы данных в моем локальном экземпляре SQL Server:

create database CompanyFoo
go

create database CompanyBar
go

create database CompanyZaz
go

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

use CompanyFoo
go

drop table ConfigurationValue
go

create table ConfigurationValue
(
    Id int not null identity(1, 1),
    Name varchar(255) not null,
    [Desc] varchar(max) not null
)
go

insert into ConfigurationValue values ('Company name', 'Foo Company')
go

use CompanyBar
go

drop table ConfigurationValue
go

create table ConfigurationValue
(
    Id int not null identity(1, 1),
    Name varchar(255) not null,
    [Desc] varchar(max) not null
)
go

insert into ConfigurationValue values ('Company name', 'Bar Company')
go

use CompanyZaz
go

drop table ConfigurationValue
go

create table ConfigurationValue
(
    Id int not null identity(1, 1),
    Name varchar(255) not null,
    [Desc] varchar(max) not null
)
go

insert into ConfigurationValue values ('Company name', 'Zaz Company')
go

следующий шаг-создать пользователя с аутентификацией SQL и предоставить доступ для чтения баз данных, в моем случае мое имя пользователя johnd и пароль 123.

как только у нас есть эти шаги завершив, мы приступаем к созданию приложения MVC в ASP.NET Core, я использовал MultipleCompany как имя проекта, у меня есть два контроллера: Home и Administration, цель состоит в том, чтобы сначала показать представление входа, а затем перенаправить на другое представление, чтобы показать данные в соответствии с выбранной базой данных в представлении "вход".

чтобы выполнить ваше требование, вам нужно будет использовать сеанс на ASP.NET Core application вы можете изменить этот способ хранения и чтения данных позже, пока это для тестирования концепции только.

код HomeController:

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using MultipleCompany.Models;

namespace MultipleCompany.Controllers
{
    public class HomeController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }

        [HttpPost]
        public IActionResult Index(LoginModel model)
        {
            HttpContext.Session.SetString("CompanyCode", model.CompanyCode);
            HttpContext.Session.SetString("UserName", model.UserName);
            HttpContext.Session.SetString("Password", model.Password);

            return RedirectToAction("Index", "Administration");
        }

        public IActionResult Error()
        {
            return View();
        }
    }
}

код AdministrationController:

using System.Linq;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using MultipleCompany.Models;
using MultipleCompany.Services;

namespace MultipleCompany.Controllers
{
    public class AdministrationController : Controller
    {
        protected IDbContextService DbContextService;
        protected CompanyDbContext DbContext;

        public AdministrationController(IDbContextService dbContextService)
        {
            DbContextService = dbContextService;
        }

        public override void OnActionExecuting(ActionExecutingContext context)
        {
            DbContext = DbContextService.CreateCompanyDbContext(HttpContext.Session.CreateLoginModelFromSession());

            base.OnActionExecuting(context);
        }

        public IActionResult Index()
        {
            var model = DbContext.ConfigurationValue.ToList();

            return View(model);
        }
    }
}

код для домашнего просмотра:

@{
    ViewData["Title"] = "Home Page";
}

<form action="/home" method="post">
    <fieldset>
        <legend>Log in</legend>

        <div>
            <label for="CompanyCode">Company code</label>
            <select name="CompanyCode">
                <option value="CompanyFoo">Foo</option>
                <option value="CompanyBar">Bar</option>
                <option value="CompanyZaz">Zaz</option>
            </select>
        </div>

        <div>
            <label for="UserName">User name</label>
            <input type="text" name="UserName" />
        </div>

        <div>
            <label for="Password">Password</label>
            <input type="password" name="Password" />
        </div>

        <button type="submit">Log in</button>
    </fieldset>
</form>

код для просмотра администрации:

@{
    ViewData["Title"] = "Home Page";
}

<h1>Welcome!</h1>

<table class="table">
    <tr>
        <th>Name</th>
        <th>Desc</th>
    </tr>
    @foreach (var item in Model)
    {
        <tr>
            <td>@item.Name</td>
            <td>@item.Desc</td>
        </tr>
    }
</table>

код LoginModel:

using System;
using Microsoft.AspNetCore.Http;

namespace MultipleCompany.Models
{
    public class LoginModel
    {
        public String CompanyCode { get; set; }

        public String UserName { get; set; }

        public String Password { get; set; }
    }

    public static class LoginModelExtensions
    {
        public static LoginModel CreateLoginModelFromSession(this ISession session)
        {
            var companyCode = session.GetString("CompanyCode");
            var userName = session.GetString("UserName");
            var password = session.GetString("Password");

            return new LoginModel
            {
                CompanyCode = companyCode,
                UserName = userName,
                Password = password
            };
        }
    }
}

CompanyDbContext код:

using System;
using Microsoft.EntityFrameworkCore;

namespace MultipleCompany.Models
{
    public class CompanyDbContext : Microsoft.EntityFrameworkCore.DbContext
    {
        public CompanyDbContext(String connectionString)
        {
            ConnectionString = connectionString;
        }

        public String ConnectionString { get; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer(ConnectionString);

            base.OnConfiguring(optionsBuilder);
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
        }

        public DbSet<ConfigurationValue> ConfigurationValue { get; set; }
    }
}

код ConfigurationValue:

using System;

namespace MultipleCompany.Models
{
    public class ConfigurationValue
    {
        public Int32? Id { get; set; }

        public String Name { get; set; }

        public String Desc { get; set; }
    }
}

код AppSettings:

using System;

namespace MultipleCompany.Models
{
    public class AppSettings
    {
        public String CompanyConnectionString { get; set; }
    }
}

IDbContextService код:

using MultipleCompany.Models;

namespace MultipleCompany.Services
{
    public interface IDbContextService
    {
        CompanyDbContext CreateCompanyDbContext(LoginModel model);
    }
}

код DbContextService:

using System;
using Microsoft.Extensions.Options;
using MultipleCompany.Models;

namespace MultipleCompany.Services
{
    public class DbContextService : IDbContextService
    {
        public DbContextService(IOptions<AppSettings> appSettings)
        {
            ConnectionString = appSettings.Value.CompanyConnectionString;
        }

        public String ConnectionString { get; }

        public CompanyDbContext CreateCompanyDbContext(LoginModel model)
        {
            var connectionString = ConnectionString.Replace("{database}", model.CompanyCode).Replace("{user id}", model.UserName).Replace("{password}", model.Password);

            var dbContext = new CompanyDbContext(connectionString);

            return dbContext;
        }
    }
}

запуск кода:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using MultipleCompany.Models;
using MultipleCompany.Services;

namespace MultipleCompany
{
    public class Startup
    {
        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
                .AddEnvironmentVariables();
            Configuration = builder.Build();
        }

        public IConfigurationRoot Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // Add framework services.
            services.AddMvc();

            services.AddEntityFrameworkSqlServer().AddDbContext<CompanyDbContext>();

            services.AddScoped<IDbContextService, DbContextService>();

            services.AddDistributedMemoryCache();
            services.AddSession();

            services.AddOptions();

            services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));

            services.AddSingleton<IConfiguration>(Configuration);
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseBrowserLink();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }

            app.UseStaticFiles();

            app.UseSession();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}

я добавил Эти пакеты для своего проекта:

"Microsoft.EntityFrameworkCore": "1.0.1",
"Microsoft.EntityFrameworkCore.SqlServer": "1.0.1",
"Microsoft.AspNetCore.Session":  "1.0.0"

мои appsettings.файл json:

{
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Debug",
      "System": "Information",
      "Microsoft": "Information"
    }
  },
  "AppSettings": {
    "CompanyConnectionString": "server=(local);database={database};user id={user id};password={password}"
  }
}

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

в принципе, нам нужно определить службу для создания экземпляра контекста БД в соответствии с выбранной базой данных, это интерфейс IDbContextService и DbContextService это реализация для этого интерфейса.

как вы можете видеть в коде DbContextService, мы заменяем значения внутри {} для построения другой строки подключения, в этом случае я добавил имена баз данных в раскрывающемся списке, но в реальной разработке, пожалуйста, избегайте этого, потому что по соображениям безопасности лучше не раскрывать реальные имена ваших баз данных и других конфигураций; вы можете иметь таблицу четности со стороны контроллера, чтобы разрешить код компании в соответствии с выбранной базой данных.

одним из улучшений для этого решения было бы добавить некоторый код для сериализации модели входа в систему как json в сеанс вместо хранения каждого значения в раздельный способ.

пожалуйста, дайте мне знать, если этот ответ полезен. PD: Дайте мне знать в комментариях, если вы хотите, чтобы полный код был загружен на одном диске


поскольку вы строите многопользовательское веб-приложение, вы должны сначала решить, как вы будете различать арендаторов. Вы собираетесь использовать differnent URL-адрес? или, может быть, тот же URL, но добавление части в URL?

предполагая, что вы выбрали последнее, поэтому у арендатора 1 будет URL-адрес, подобный этому:http://localhost:9090/tenant1/orders

арендатор 2 будет иметь URL-адрес, например:http://localhost:9090/tenant2/orders

вы можно сделать это с помощью маршрутизации URL:

 routes.MapRoute(
                name: "Multitenant",
                url: "{tenant}/{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );

Что касается строки подключения, вам нужен класс, чтобы решить строку подключения на основе URL-адреса и ввести этот класс в контекст БД.

public interface ITenantIdentifier
{
 string GetCurrentTenantId();
}

public class UrlTenantIdentifier : ITenantIdentifier
{
  public string GetCurrentTenantId()
  { 
    //Get the current Http Context and get the URL, you should have a table or configration that maps the URL to the tenant ID and connection string
  }
}

в контексте вашей БД:

public class MyDbContext: DbContext
{
 public MyDbContext(ITenantIdentifier tenantIdentifier)
 { 
   var connectionStringName = "TenantConnectionString"+tenantIdentifier.GetCurrentTenantId(); //here assuming that you are following a pattern, each tenant has a connection string in the shape of TenantConnectionString+ID

  var connectionString = //get connection string
  base(connectionString);
 }
}

обновление для передачи строки подключения

чтобы передать динамически сгенерированное соединение в контекст, создайте частичный класс в том же контексте, что и ваш контекст-частичный класс гарантирует, что он останется нетронутым, если кто-то запустил пользовательский инструмент (для edmx), автоматически сгенерированный код будет уничтожен и восстановлен. Если у вас есть этот код в частичном классе, он не будет уничтожен. Для кода first это не будет применяться. Вот код:

public class YourContext : DbContext
{
    public YourContext(string connString)
    {

    }

}

путь Я сделал это в прошлом, чтобы иметь одну базу данных, где хранятся учетные записи (имена пользователей, пароли) всех клиентов. Учетная запись, под которой работает приложение, будет использоваться для связи с этой базой данных для аутентификации клиента, который ведет журнал (CompanyID, Password).

после этого, после аутентификации, генерируется токен. После этого аутентифицированный пользователь будет взаимодействовать с базой данных этого клиента (компании). Для этой части можно создать соединение на муха, как показано здесь но я скопирую и вставлю ее тоже:

// Specify the provider name, server and database.
string providerName = "System.Data.SqlClient";
string serverName = ".";
string databaseName = "AdventureWorks";

// Initialize the connection string builder for the
// underlying provider.
SqlConnectionStringBuilder sqlBuilder =
    new SqlConnectionStringBuilder();

// Set the properties for the data source.
sqlBuilder.DataSource = serverName;
sqlBuilder.InitialCatalog = databaseName;
sqlBuilder.IntegratedSecurity = true;

// Build the SqlConnection connection string.
string providerString = sqlBuilder.ToString();

// Initialize the EntityConnectionStringBuilder.
EntityConnectionStringBuilder entityBuilder =
    new EntityConnectionStringBuilder();

//Set the provider name.
entityBuilder.Provider = providerName;

// Set the provider-specific connection string.
entityBuilder.ProviderConnectionString = providerString;

// Set the Metadata location.
entityBuilder.Metadata = @"res://*/AdventureWorksModel.csdl|
                res://*/AdventureWorksModel.ssdl|
                res://*/AdventureWorksModel.msl";
Console.WriteLine(entityBuilder.ToString());

вам нужно будет предоставить свои собственные имена csdl, ssdl и msl в приведенном выше коде. Если сначала используется код, то строка подключения не будет нуждаться в метаданных.


вы можете попробовать следующее при создании экземпляра контекста:

// in class DBHelper
public static YourEntities GetDbContext(string tenantName) 
{
    var connectionStringTemplate =
      @"metadata=res://*/yourModel.csdl|res://*/yourModel.ssdl|res://*/yourModel.msl;" +
      @"provider=System.Data.SqlClient;" +
      @"provider connection string=""data source=.;" +
      @"initial catalog={0};" +
      @"user id=sa;password=pwd;" +
      @"MultipleActiveResultSets=True;App=EntityFramework"";";

     var connectionString = string.Format(connection, tenantName);
     var db = new YourEntities(connectionString);
     return db;
}

затем создайте конструктор в своем классе контекста, который принимает string в качестве параметра и использует его как:

var db = DBHelper.GetDbContext(name of database to connect);