C# 实体框架-在运行时更改连接字符串(需要解释)

C# 实体框架-在运行时更改连接字符串(需要解释),c#,entity-framework,connection-string,C#,Entity Framework,Connection String,我很抱歉将此作为一个问题发布,但我还无法在我的问题的实际解决方案下发表评论,该解决方案已被回答。也不以同样的方式工作 我使用了这个解决方案,除了实际更改连接之外,扩展似乎还能自己工作。它与web.config文件中定义的相同。若我删除那个连接字符串,我会得到一个错误,说EF找不到它 我的方法是先使用数据库(而且是SQL Server 2000…)和EF版本6(基本上是最新的) 所以我的问题是-它应该如何工作 我是否必须向扩展方法传递与web.config中定义的相同的连接名称,还是应该不同

我很抱歉将此作为一个问题发布,但我还无法在我的问题的实际解决方案下发表评论,该解决方案已被回答。也不以同样的方式工作

我使用了这个解决方案,除了实际更改连接之外,扩展似乎还能自己工作。它与
web.config
文件中定义的相同。若我删除那个连接字符串,我会得到一个错误,说EF找不到它

我的方法是先使用数据库(而且是SQL Server 2000…)和EF版本6(基本上是最新的)

所以我的问题是-它应该如何工作

  • 我是否必须向扩展方法传递与
    web.config
    中定义的相同的连接名称,还是应该不同
我当前的连接字符串如下所示:

<connectionStrings>
    <add name="CATALOGEntities" connectionString="metadata=~/bin/Models\InfoModel.csdl|~/bin/Models\InfoModel.ssdl|~/bin/Models\InfoModel.msl;provider=System.Data.SqlClient;provider connection string=&quot;data source=SERVER;initial catalog=CATALOG;integrated security=False;User Id=admin;Password=admin123;MultipleActiveResultSets=True;App=EntityFramework&quot;" providerName="System.Data.EntityClient" />
</connectionStrings>
尝试2:也按照VDohnal的建议进行了尝试:

db.ChangeDatabase("ANOTHERCATALOG", "ANOTHERSERVER", "admin", "admin456", false, "ANOTHERCATALOGEntities"); 
与:

  • 另外,我应该向扩展方法传递什么数据源—整个
    DbContext
    ,还是在我使用的控制器类中定义的数据源,即
    CATALOGEntities
下面是我正在使用的扩展方法:

public static class ConnectionTools
{
    // all params are optional
    public static void ChangeDatabase(
        this CATALOGEntities source,
        string initialCatalog = "",
        string dataSource = "",
        string userId = "",
        string password = "",
        bool integratedSecuity = false,
        string configConnectionStringName = "")
    /* this would be used if the
    *  connectionString name varied from 
    *  the base EF class name */
    {
        try
        {
            // use the const name if it's not null, otherwise
            // using the convention of connection string = EF contextname
            // grab the type name and we're done
            var configNameEf = string.IsNullOrEmpty(configConnectionStringName)
                ? source.GetType().Name
                : configConnectionStringName;

            // add a reference to System.Configuration
            var entityCnxStringBuilder = new EntityConnectionStringBuilder
                (System.Configuration.ConfigurationManager
                    .ConnectionStrings[configNameEf].ConnectionString);

            // init the sqlbuilder with the full EF connectionstring cargo
            var sqlCnxStringBuilder = new SqlConnectionStringBuilder
                (entityCnxStringBuilder.ProviderConnectionString);

            // only populate parameters with values if added
            if (!string.IsNullOrEmpty(initialCatalog))
                sqlCnxStringBuilder.InitialCatalog = initialCatalog;
            if (!string.IsNullOrEmpty(dataSource))
                sqlCnxStringBuilder.DataSource = dataSource;
            if (!string.IsNullOrEmpty(userId))
                sqlCnxStringBuilder.UserID = userId;
            if (!string.IsNullOrEmpty(password))
                sqlCnxStringBuilder.Password = password;

            // set the integrated security status
            sqlCnxStringBuilder.IntegratedSecurity = integratedSecuity;

            // now flip the properties that were changed
            source.Database.Connection.ConnectionString
                = sqlCnxStringBuilder.ConnectionString;
        }
        catch (Exception ex)
        {
            // set log item if required
        }
    }
}
我的数据库上下文:

public partial class CATALOGEntities : DbContext
{
    public CATALOGEntities()
        : base("name=CATALOGEntities")
    {
    }
}

查看链接到的答案中的代码,它所做的只是从
web.config
文件中读取连接字符串,然后使用
SqlConnectionStringBuilder
类用新的详细信息替换连接字符串的相关部分

它不会将修改后的连接字符串写回
web.config
。您可以将现有连接字符串视为模板

我怀疑您希望传递与控制器相关的上下文,最后一个参数将是当前连接字符串的名称(除非它与您的上下文同名-在这种情况下,您可以忽略它)


当然,这一切都假设另一个数据库具有相同的模型。

您必须传递一个已经在.config中存在的连接名,或者忽略它。 所以这样称呼它:

 db.ChangeDatabase( "ANOTHERCATALOG", "ANOTHERSERVER", "admin", "admin456", false);
它不会更改应用程序的初始配置连接,只会在运行时更改DbContext(
CATALOGEntities
)的特定现有实例的连接。我认为,这不是您所需要的——无论何时创建新的DbContext,您都需要调用它


我建议您使用不同的方法。创建一个工厂,根据所选国家/地区生成DbContext实例。无论何时创建新的DbContext,都使用该工厂。另一种方法是更改DbContext(
CATALOGEntities
)类的构造函数。

解决方案:这就是最终对我有效的方法

负责访问SQL Server的控制器类:

public class FrequentlyAccessedQueries : Controller
{
    private CATALOGEntities db = FrequentlyAccessedQueries.entities();

    public static CATALOGEntities entities()
    {
        QueryDetails qdetails = new QueryDetails();
        bool uk = qdetails.IsCountryUK;
        if (uk) 
        {
            return new CATALOGEntities("name=CATALOGEntitiesUK");
        }
        else 
        {
            return new CATALOGEntities("name=CATALOGEntitiesUSA");
        }
    }
}
DbContext类:

public partial class CATALOGEntities : DbContext
{
    public CATALOGEntities(string connectionString)
        : base(connectionString)
    {
    }
}
web.config条目:

<connectionStrings>
    <add name="CATALOGEntitiesUK" connectionString="[...]" providerName="System.Data.EntityClient" />
    <add name="CATALOGEntitiesUSA" connectionString="[...]" providerName="System.Data.EntityClient" />
</connectionStrings>

更改数据库连接的另一种方法是动态更改ConfigurationManager连接字符串。
这有点像黑客,但允许您轻松地通过几个不同的数据库(Dev、Test、Prod)。它确实要求连接到的每个数据库中都有相同的表。 如果您需要更改连接字符串中的任何其他内容,希望这将是一个良好的开端

string DataBaseName = "bab"
string applicationName = Environment.GetCommandLineArgs()[0] ;
string exePath = System.IO.Path.Combine(Environment.CurrentDirectory, applicationName);
var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
var connectionStringsSection = (ConnectionStringsSection)config.GetSection("connectionStrings");
//excuse the poor regex - I'm still figuring it out
connectionStringsSection.ConnectionStrings["Entities"].ConnectionString =
Regex.Replace(connectionStringsSection.ConnectionStrings["Entities"].ConnectionString, "initial catalog.*;(i)", "initial catalog ="+DataBaseName+";i");
config.Save();
ConfigurationManager.RefreshSection("connectionStrings");

Entities test = new Entities();
IEnumerable<int> list = from bobble in test.bobble
                                where bobble.ID < 250
                                select bobble.ID;
string DataBaseName=“bab”
字符串applicationName=Environment.GetCommandLineArgs()[0];
字符串exePath=System.IO.Path.Combine(Environment.CurrentDirectory,applicationName);
var config=ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
var connectionStringsSection=(connectionStringsSection)config.GetSection(“connectionStrings”);
//原谅可怜的正则表达式-我还在想办法
ConnectionString节。ConnectionString[“实体”]。ConnectionString=
Regex.Replace(connectionStringsSection.ConnectionStrings[“实体”]。ConnectionStrings,“初始目录。*;(i)”,“初始目录=“+DataBaseName+”;“i”);
config.Save();
ConfigurationManager.RefreshSection(“连接字符串”);
实体测试=新实体();
IEnumerable list=来自test.bobble中的bobble
其中bobble.ID<250
选择bobble.ID;

我做了所有这些,source.Database.Connection.ConnectionString设置正确,但当我实际使用它读取表时,它失败了,因为它从Web.Config读取了一个已屏蔽随机凭据的表。我正在运行时更改密码和用户ID,就像尝试2一样。有人让它工作了吗

 mycontext.ChangeDatabase
            (
                userId: something,
                password: something

            );
      string b= mycontext.Table1
                        .Where(u => u.name == 'a')
                        .Select(a => a.ID)
                        .FirstOrDefault();

谢谢你,布伦丹。我同意你的假设,包括关于另一个DB具有相同模型的假设。两个数据库之间的唯一区别是,它们服务于不同的国家,因此它们保存的数据不同,但模型保持不变。当我显式设置DB参数时,我的应用程序运行良好。然而,它仍然没有改变这种联系——在我看来,我得到了完全相同的结果。谢谢你,@VDohnal——仍然没有乐趣。它可以工作,但不会切换到不同的服务器/DB。另一种方法是更改DbContext(=CATALOGEntities)类的构造函数。-这就是我试图避免的:)好的,我会看看是否能按照你的建议来解决。不过,如果上面的解决方案能起作用,那就太好了。您是否在创建新的DbContext后立即调用它?我有
private CATALOGEntities db=new CATALOGEntities()在我的控制器类的顶部。我还尝试实例化
CATALOGEntities db=newCatalogEntities()if/else
语句中调用
db.ChangeDatabase(…)
,但结果相同。您的模型是db first吗?EF版本呢?对不起,应该提到的。是的,我的方法是DB优先(而且是SQL Server 2000…)和EF版本6(基本上是最新的)。如果有帮助的话,我还在上面添加了DbContext内容
<connectionStrings>
    <add name="CATALOGEntitiesUK" connectionString="[...]" providerName="System.Data.EntityClient" />
    <add name="CATALOGEntitiesUSA" connectionString="[...]" providerName="System.Data.EntityClient" />
</connectionStrings>
string DataBaseName = "bab"
string applicationName = Environment.GetCommandLineArgs()[0] ;
string exePath = System.IO.Path.Combine(Environment.CurrentDirectory, applicationName);
var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
var connectionStringsSection = (ConnectionStringsSection)config.GetSection("connectionStrings");
//excuse the poor regex - I'm still figuring it out
connectionStringsSection.ConnectionStrings["Entities"].ConnectionString =
Regex.Replace(connectionStringsSection.ConnectionStrings["Entities"].ConnectionString, "initial catalog.*;(i)", "initial catalog ="+DataBaseName+";i");
config.Save();
ConfigurationManager.RefreshSection("connectionStrings");

Entities test = new Entities();
IEnumerable<int> list = from bobble in test.bobble
                                where bobble.ID < 250
                                select bobble.ID;
 mycontext.ChangeDatabase
            (
                userId: something,
                password: something

            );
      string b= mycontext.Table1
                        .Where(u => u.name == 'a')
                        .Select(a => a.ID)
                        .FirstOrDefault();