C# 如何在asp.net mvc中动态更改连接字符串

C# 如何在asp.net mvc中动态更改连接字符串,c#,asp.net,asp.net-mvc,entity-framework,C#,Asp.net,Asp.net Mvc,Entity Framework,在我们公司,我们有一个应用程序是为1数据库。现在,我被告知要为每个客户公司建立多租户数据库。所以我建立了一个新的数据库,我将在其中存储所有用户和他们的公司名称,我将使用它们来更改数据库。 我想做的是: 1.用户登录 2.后端检查用户的公司名称 3.检索到的公司名称将分配给dbcontext:base,它将使用公司名称切换数据库 当然,我在stackoverflow和其他地方查看了与此相关的其他问题,其中大多数都将此作为解决方案: public FacilityEntities() : b

在我们公司,我们有一个应用程序是为1数据库。现在,我被告知要为每个客户公司建立多租户数据库。所以我建立了一个新的数据库,我将在其中存储所有用户和他们的公司名称,我将使用它们来更改数据库。 我想做的是: 1.用户登录 2.后端检查用户的公司名称 3.检索到的公司名称将分配给dbcontext:base,它将使用公司名称切换数据库

当然,我在stackoverflow和其他地方查看了与此相关的其他问题,其中大多数都将此作为解决方案:

    public FacilityEntities() : base("name=Demo") { }
    public FacilityEntities(string dbConnection) : base(dbConnection)
    {
    }
大多数人都说这是可行的。但这对我不起作用。 另外,虽然不建议这样做,但我也尝试在运行时更改web.config文件,但每次用户登录时,应用程序都会刷新,无法完成登录过程

我现在拥有的代码:

登录

        public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
    {
        if (!ModelState.IsValid)
        {
            return View(model);
        }

        // This doesn't count login failures towards account lockout
        // To enable password failures to trigger account lockout, change to shouldLockout: true
        var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);

        switch (result)
        {
            case SignInStatus.Success:
                returnUrl = CheckFirstLogin(model.Email, returnUrl);
                using (var context = new Facility.AdgarWeb.Models.UserCommonDBContext())
                {
                    var companyName = context.CommonUser.Where(x => x.CommonUserEmail == model.Email).FirstOrDefault().CommonUserCompanyName;

                    new FacilityEntities(companyName.ToString());

                }
                        await OnSignInSuccess(model);
                //FormsAuthentication.SetAuthCookie(model.Email, false);
                return RedirectToLocal(returnUrl);
            case SignInStatus.LockedOut:
                return View("Lockout");
            case SignInStatus.RequiresVerification:
                return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
            case SignInStatus.Failure:
            default:
                ModelState.AddModelError("", "Invalid login attempt.");
                return View(model);
        }
}

当我在上设置调试点时 一,

及 二,


我可以看到,应用程序首先点击第一个代码,然后点击第二个代码,但最终会点击第一个

我还发现我有DbFactory文件: 数据库工厂

        FacilityEntities dbContext;
    public FacilityEntities Init()
    {
        return dbContext ?? (dbContext = new FacilityEntities());
    }

    protected override void DisposeCore()
    {
        if (dbContext != null)
            dbContext.Dispose();
    }
}
请任何人帮我解决这个问题

更新 我知道我可以这样使用它:

public FacilityEntities() : base("name=Demo") { }
public FacilityEntities(string dbConnection) : base(dbConnection)
{
}


但是,如何设置更改后的数据库而不必使用(){}?如何让登录的用户一直使用此数据库?每次我用db做某事时,我都必须调用这个新db吗?有没有办法将此已更改的数据库设置为此登录用户的“主数据库”?

对于此问题,我只需将新实体的第二个连接字符串添加到web.config

<connectionStrings>
 <add name="DefaultConnection" .../>
 <add name="DefaultConnection2" .../>
</connectionStrings


您不一定需要从web.config获取连接字符串。您可以使用此代码直接传递连接字符串

public FacilityEntities(string connString)
{
    this.Database.Connection.ConnectionString = connString;
}
或者,如果所有租户的连接字符串相同,但只有数据库名称不同,则可以在
web.config
中添加连接字符串,并用
CompanyName
替换db名称

Web.Config

<connectionStrings>
  <add name="FacilityEntities" connectionString="Server=.;Database=_DBNAME_;User Id=myUsername; Password=myPassword;" providerName="System.Data.SqlServerCe.4.0"/>    
</connectionStrings>

这样,您可以动态选择数据库名称。

您的dbcontext类有2个构造函数。1个默认值和1个参数化值。您需要调用参数化版本

return dbContext ?? (dbContext = new FacilityEntities());
试着把它改成

return dbContext ?? (dbContext = new FacilityEntities("New Connection String"));
无论哪种情况,您都必须修改连接代码

根据的好建议,创建多个连接字符串,我发现它的工作方式如下:

public FacilityEntities() : base("name=Demo") { } // parameterless
public FacilityEntities(string dbConnection) : base($"name={dbConnection}") { } // with param
唯一缺少的东西是
name=…
基本构造函数中

现在您可以通过以下方式使用它(简化代码):

注意:由于FacilityEntities可能是由生成的c#code组成的EDMX的一部分,因此最好将参数构造函数添加为同一命名空间中的单独c#文件(例如
FacilityEntities.cs
),该命名空间包含
部分

namespace YourNameSpace // important: use the same namespace your EDMX is using
{
    public partial class FacilityEntities 
    {
        public FacilityEntities(string dbConnection) : base($"name={dbConnection}") { } 
    }
}

无参数构造函数已经存在(在生成的代码中),因此您只能指定带有参数的构造函数。

但是如何选择要使用的构造函数?我不想要数据库名硬编码。我想根据用户登录的情况更改数据库。@akyj“如何选择要使用的数据库”是一个业务规则问题。我会咨询你的客户或PM。你可以用一个简单的if来改变用户的DB。@ AKYJ考虑把这个问题标记为答案,然后你应该为你的第二个问题发布一个新的、独立的、具体的问题。一旦你发布了新的问题,我很乐意看一看。如何使用额外的连接字符串生成EF,你可以找到。我已经在我自己的一个项目中使用了它,它工作得很好。我同意@C的建议,在您的代码中,根据您从登录中获得的用户的公司信息确定要使用哪个连接字符串。你说每个公司一个数据库,对吗?我已经在web.config中有了所有的数据库连接字符串。我希望用户登录,后端检索用户的companyName(这也是webconfig中ConnectionString中列出的数据库名称)。新设施实体(companyName.ToString());在登录代码中,将company name放入dbcontext,并且应该从web.config中选择正确的数据库,但是当您说“separate database”时,它不起作用。您是指一个单独的SQL Server实例还是实例中有多个数据库?或者你不是在使用SQL Server,而是在使用其他东西?@JohnWu我有一个mysql实例,其中有几个数据库被命名为用户公司名称。“我可以看到,应用程序首先命中第一个代码,然后命中第二个代码,但最终命中第一个。”它应该这样做。连接设置不正确吗?问题是什么?在第二个选项中,我如何处理公共设施entities():base(“name=demo”)?我无法删除它而不出错。在调试控制台中调试时,当我键入this.Database.Connection.ConnectionString时,它返回一个正确的值,但不会切换到该数据库如果我删除默认构造函数,我会出错,因为我有一个dbfactory,其中返回了dbContext??(dbContext=new FacilityEntities())不接受任何参数。我认为你的答案很接近,但应用程序在最终点击了你提供的代码后,最终点击了带有演示数据库的默认构造函数。你仍然可以拥有
公共设施实体():base(“name=demo”){}
因此,您的DbContextFactory调用此构造函数,但数据库保持为演示状态,不会更改为具有公司名称的数据库。如果我在两个构造函数上都设置了调试点,应用程序将点击您的代码,调试控制台将显示正确的连接字符串。但在这之后,它会点击name=demo的默认构造函数并将数据库设置为demo我不知道我们的
dbContextFactory
的意义,如果您一直使用它,
dbContextFactory
应该传递公司名称并调用t
public FacilityEntities(string companyName)
{
    string connString = ConfigurationManager.ConnectionStrings["FacilityEntities"].ConnectionString;
    connString = connString.Replace("_DBNAME_", companyName);
    this.Database.Connection.ConnectionString = connString;
} 
return dbContext ?? (dbContext = new FacilityEntities());
return dbContext ?? (dbContext = new FacilityEntities("New Connection String"));
public FacilityEntities() : base("name=Demo") { } // parameterless
public FacilityEntities(string dbConnection) : base($"name={dbConnection}") { } // with param
var dbConnection = "DefaultConnection2";
using (var ctx = new FacilityEntities(dbConnection))
{
   // any EF queries using ctx go here
}
namespace YourNameSpace // important: use the same namespace your EDMX is using
{
    public partial class FacilityEntities 
    {
        public FacilityEntities(string dbConnection) : base($"name={dbConnection}") { } 
    }
}