将多个数据库与单个DbContext和Entites一起使用,并在运行时生成Conn String

我正在开发一个MVC 5应用程序。 最初我使用单一数据库和EF6 DataBase第一种方法,我使用我的DbContext实例来访问我的数据库,它有102个表。 我将其实例声明为:

private MyEntities db = new MyEntities();

现在我想允许多家公司使用我的应用程序,为此我必须为每个新公司创建一个新的数据库。 我通过向DbContext声明另一个构造函数来实现此目的,如下所示:

public partial class NewEntities : DbContext { public NewEntities(string name) : base(name) { } } 

然后将其实例声明为:

 public NewEntities de = new NewEntities((ConfigurationManager.ConnectionStrings["NewEntities123"]).ToString()); 

然后我打电话

db.Database.Create();

我的新数据库已成功创建。 但是在这个方案中,每次我想添加数据库时,我都必须在我的Web.config文件中声明一个新的连接字符串。

是否有任何方法根据配置文件中的公司名称自动生成连接字符串,然后传递给构造函数以创建具有该名称的新数据库?

此外,我想为每个公司使用相同的“db”实例和DbContext访问我的所有控制器方法,以便我的相同代码可以用于所有公司。 如何为多个数据库访问单个DbContext及其实例?

我已经看过这个答案,因为它说没有方法。 但那么我的应用程序如何适用于多个用户?

在数据库优先工作时,拥有带映射的edmx文件,您可以通过这种方式创建和使用数据库。

为您的上下文构造函数添加一个重载(就像您一样),例如:

 public TenantDBContainer(string connectionString) : base(connectionString) { } 

然后,当您需要创建数据库(如果不存在)时,使用yourContextInstance.Database.CreateIfNotExists();

 var connectionStringTemplate = @"metadata=res://*/Models.TenantDB.csdl|res://*/Models.TenantDB.ssdl|res://*/Models.TenantDB.msl;" + @"provider=System.Data.SqlClient;" + @"provider connection string=""data source=(localdb)\v11.0;" + @"initial catalog={0};"+ @"user id={1};password={2};" + @"MultipleActiveResultSets=True;App=EntityFramework"";"; var TenandDBName = "Database Name Based on Tenant"; var TenantUserName = "UserName Based on Tenant"; var TenantPassword = "Password Based on Tenant"; var connectionString = string.Format(connectionStringTemplate, TenandDBName, TenantUserName, TenantPassword); var db = new TenantDBEntities(connectionString); db.Database.CreateIfNotExists(); 

注意:

  • 这样,在多租户应用程序中,您可以为每个租户创建数据库并使用它。
  • 您无需更改webconfig以在其中添加连接字符串,您只需在运行时创建连接字符串即可。
  • 现在,您可以根据租户检测策略设计结构以获取上下文

作为简化案例的示例,假设您有一个静态方法,可以为您返回上下文实例,例如:

 Public class DbHelper { public static TenantDBEntities GetDbContext(string tenantName) { var connectionStringTemplate = @"metadata=res://*/Models.TenantDB.csdl|res://*/Models.TenantDB.ssdl|res://*/Models.TenantDB.msl;" + @"provider=System.Data.SqlClient;" + @"provider connection string=""data source=(localdb)\v11.0;" + @"initial catalog={0};"+ @"integrated security=True;" + @"MultipleActiveResultSets=True;App=EntityFramework"";"; var TenandDBName = "TenantDB_" + tenantName; var connectionString = string.Format(connectionStringTemplate, TenandDBName); var db = new TenantDBEntities(connectionString); db.Database.CreateIfNotExists(); return db; } } 

并且假设我的租户dettection策略仅仅基于请求URL或其他东西。

现在,例如在Index动作中的ProductController中,您可以这样使用它;

 public ActionResult Index() { var tenantName = "Get tenant based on your strategy, for example tenant1 or tenant2"; var db= DbHelper.GetDbContext(tenantName) var model= db.Products.ToList(); return View(model); } 

当每个租户使用此操作时,它将连接到适合该租户的数据库,如果数据库不存在,则首先创建它然后使用它。

这样每个租户都会看到自己的产品。

您可以使用工厂做类似于您尝试的操作。 如果没有指定您尝试连接的数据库,就没有其他方法可以将一个DbContext用于多个数据库。 您尝试执行的操作的问题是上下文中的实体需要存在于两个数据库中。 我认为你真正想要的是一个生产多个DbContext类的工厂(第二个例子)。

  void Main() { var factory = new DBFactory(); var context = factory.GetContext("NewEntities123"); var context2 = factory.GetContext("SomeOtherDatabase"); } public class DBFactory { public DBContext GetContext(string dbName) { return new NewEntities((ConfigurationManager.ConnectionStrings[dbName]).ToString()); } } void Main() { //instead of passing in the database name here you could //just use a single config value for a database and that //database would be different for your different apps //then it would just be var context = factory.GetContext(); var context = factory.GetContext("NewEntities123"); } public class DBFactory { public DBContext GetContext(string dbName) { switch(dbName) { case "NewEntities123": return new NewEntities((ConfigurationManager.ConnectionStrings[dbName]).ToString()); case "SomeOtherDatabase": return new SomeOtherEntities((ConfigurationManager.ConnectionStrings[dbName]).ToString()); } } } 

传递到DbContext构造函数的参数可以是名称或连接字符串。 如果您只是在某处生成连接字符串并在创建类时将其传入,则所有内容都应正常工作。