使用具有单个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及其实例?

我已经看过这个问题的答案,因为它说没有方法。但是,我的应用程序如何为多个用户工作?

使用具有单个DbContext和实体的多个数据库,并在运行时生成Conn字符串

当使用数据库优先,有一个带有映射的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构造函数的参数可以是一个名称,也可以是一个连接字符串。如果您只是在某个地方生成连接字符串,并在创建类时传递它,那么一切都应该正常工作。