使用具有单个DbContext和实体的多个数据库,并在运行时生成Conn字符串
本文关键字:数据库 运行时 字符串 Conn 单个 DbContext 实体 | 更新日期: 2023-09-27 18:15:11
我正在开发一个MVC 5应用程序。最初,我使用单一数据库与EF6数据库的第一种方法,我使用我的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中声明一个新的连接字符串。配置文件,每次我想添加一个数据库。
是否有任何方法,其中一个连接字符串根据配置文件中的公司名称自动生成,然后传递给构造函数创建一个新的数据库与该名称?
此外,我想使用相同的"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();
注意:
- 这样,在多租户应用程序中,您可以为每个租户创建数据库并使用它。
- 你不需要改变你的web配置来添加连接字符串,你可以简单地在运行时创建连接字符串 现在,您可以根据租户检测策略设计一个结构来获取上下文。
作为一个简化的例子,假设您在某个地方有一个静态方法,它为您返回上下文的实例,例如:
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;
}
}
假设我的租户检测策略只是基于请求url或其他东西。
现在,例如在ProductController中的Index action中,你可以这样使用它;
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构造函数的参数可以是一个名称,也可以是一个连接字符串。如果您只是在某个地方生成连接字符串,并在创建类时传递它,那么一切都应该正常工作。