如何在.NET中以编程方式设置连接字符串配置?

63

我想以编程的方式设置连接字符串,而不对任何配置文件/注册表键进行更改。

我有这段代码,但不幸的是它会抛出一个异常,指出“配置是只读的”。

ConfigurationManager.ConnectionStrings.Clear();
string connectionString = "Server=myserver;Port=8080;Database=my_db;...";
ConnectionStringSettings connectionStringSettings = 
  new ConnectionStringSettings("MyConnectionStringKey", connectionString);
ConfigurationManager.ConnectionStrings.Add(connectionStringSettings);

编辑: 问题是我有现有的代码从配置文件中读取连接字符串。因此手动设置配置字符串或通过资源设置似乎不是有效的选项。我真正需要的是以编程方式修改配置。


你想要在配置文件中更改连接字符串吗?如果你想动态创建一个新的连接并构造任何连接字符串,那很容易实现。但是,如果你想要写入实际的配置文件,那就是另外一个问题了。 - Charles Bretana
9个回答

118

我在我的博客文章中谈到过这个问题(链接)。诀窍是使用反射机制将值作为一种获取非公共字段(和方法)访问权限的方式。

例如:

var settings = ConfigurationManager.ConnectionStrings[ 0 ];

var fi = typeof( ConfigurationElement ).GetField( "_bReadOnly", BindingFlags.Instance | BindingFlags.NonPublic );

fi.SetValue(settings, false);

settings.ConnectionString = "Data Source=Something";

1
如果你正在使用 NHibernate,请参阅此链接:http://nhforge.org/wikis/howtonh/dynamically-change-user-info-in-connection-string.aspx - David Gardiner
8
很好的解决方案!如果你想添加新的连接字符串,使用以下方法来启用添加到ConnectionString集合中:typeof(ConfigurationElementCollection).GetField("bReadOnly", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(ConfigurationManager.ConnectionStrings, false); - Allon Guralnek
12
使用反射来改变属性为可写状态感觉有些不妥,但是它真的有效! - Brian
1
@BrianV - 如果它有67个赞就不是错误的 :-D - 当这种机制被引入时,情况已经大不相同了,那时是2005年。 - Simon_Weaver
2
这些东西是必要的事实是我讨厌 .NET 的原因。@Simon_Weaver 不是说它不起作用,而是...道义上 错误。 - jpmc26
显示剩余5条评论

9
另一种处理方式是直接操作集合本身:
var settings = ConfigurationManager.ConnectionStrings;
var element = typeof(ConfigurationElement).GetField("_bReadOnly", BindingFlags.Instance | BindingFlags.NonPublic);
var collection = typeof(ConfigurationElementCollection).GetField("bReadOnly", BindingFlags.Instance | BindingFlags.NonPublic);

element.SetValue(settings, false);
collection.SetValue(settings, false);

settings.Add(new ConnectionStringSettings("ConnectionStringName", connectionString));

// Repeat above line as necessary

collection.SetValue(settings, true);
element.SetValue(settings, true);

这个很好用,但是和Andrew McClellan上面的答案类似,在 .Net 5(可能也是2.1+)中,两个都需要在GetField中设置为_readOnly。 - Brandon Barkley

8
我曾经寻找过与允许用户通过选择本地SQL Server在单击应用程序中修改连接字符串的相同问题的答案。
下面的代码显示一个用户表单,联系所有可用的本地SQL服务器并允许他们选择一个。然后它构造了该服务器的连接字符串,并从表单变量中返回它。代码然后修改配置文件并保存它。
string NewConnection = "";
// get the user to supply connection details
frmSetSQLConnection frm = new frmSetSQLConnection();
frm.ShowDialog();
if (frm.DialogResult == DialogResult.OK)
{
    // here we set the users connection string for the database
    // Get the application configuration file.
    System.Configuration.Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
    // Get the connection strings section.
    ConnectionStringsSection csSection = config.ConnectionStrings;
    foreach (ConnectionStringSettings connection3 in csSection.ConnectionStrings)
    {
        // Here we check for the preset string - this could be done by item no as well
        if (connection3.ConnectionString == "Data Source=SQL204\\SQL2008;Initial Catalog=Transition;Integrated Security=True")
        {
             // amend the details and save
             connection3.ConnectionString = frm.Connection;
             NewConnection = frm.Connection;
             break;
        }
    }
    config.Save(ConfigurationSaveMode.Modified);
    // reload the config file so the new values are available

    ConfigurationManager.RefreshSection(csSection.SectionInformation.Name);

    return clsDBMaintenance.UpdateDatabase(NewConnection))
}

请注意,这会导致您的应用程序域重新加载,因此请谨慎使用。 - yano

7
我发现这对我有用:

我发现这对我有用:

Configuration config = WebConfigurationManager.OpenWebConfiguration("~");
ConnectionStringsSection section = config.GetSection("connectionStrings") as         ConnectionStringsSection;
if (section != null)
{
    section.ConnectionStrings["MyConnectionString"].ConnectionString = connectionString;
    config.Save();
}

这将覆盖现有的连接字符串。


3
改动 web.config 文件会导致工作进程重启,因此需要小心。我试图在 ApplicationStart 事件中将 Connectionstring 设置为基于文件的数据库,但这导致了每访问一个页面都会触发应用程序重启的问题。 - Thomas
确实。我将其用作管理工具函数,用于在共享托管帐户上加密和解密web.config。正如Thomas所指出的那样,您不会希望将其作为会话或应用程序启动的一部分来执行。 - Rebecca
1
+1: 这非常适合我的目的(一个集成测试)。但是,因为我的生产代码使用了 ConfigurationManager,所以一旦我更新了测试中的部分,我必须执行 ConfigurationManager.RefreshSection("connectionStrings"); - Alex Humphrey
@AlexHumphrey 如果你使用Remove()和Add()而不是索引和赋值,就不必这样做。我没有验证过,但我知道我使用Clear()和Add(),它对我很有效。 - Neo
1
这可能在ASP.NET下工作(我不是很确定),但它绝对不能从任何不自动重新加载的可执行文件中工作(例如,控制台应用程序)。配置已在运行之前加载,这意味着连接字符串永远不会更改。 - jpmc26
默认情况下,Web应用程序安装后,Web.config文件没有写入权限。因此,现在不清楚您想要如何调用Save()方法?因为它总是报告“访问被拒绝”错误。而且,打开Web应用程序文件夹进行编写 - 那是非常糟糕的主意。所以,Save()方法听起来像是一个不好的解决方案。 - dmitry_bond

5
我目前正在使用依赖注入来处理在开发/生产环境和测试环境中不同的连接字符串。如果我想在开发和生产环境之间切换,仍然需要手动更改webconfig,但是对于测试,我有一个IConnectionStringFactory接口,其中包含一个默认实现,该实现查看web config和另一种备用测试配置,返回静态值。这样,当我进行测试时,我只需设置工厂为测试实现,它就会返回我请求的测试连接字符串的密钥。否则,它将查找webconfig。
我可以将其扩展到另一个实现以适用于开发和生产,但我更喜欢将IConnectionStringFactory的单个实现放在我的生产程序集中,将测试实现放在我的测试程序集中。

3

看起来自 .net Core 2.1 以后命名方式已经改变了

修改 David Gardiner 的回答:

以下方式可以用于引用新旧版本:

var settings = ConfigurationManager.ConnectionStrings[ 0 ];

最初的回答:

var fi = typeof( ConfigurationElement ).GetField( "_bReadOnly", BindingFlags.Instance | BindingFlags.NonPublic );

if(fi == null)
{
  fi = typeof(System.Configuration.ConfigurationElementCollection).GetField("_readOnly", BindingFlags.Instance | BindingFlags.NonPublic);
}

fi.SetValue(settings, false);

settings.ConnectionString = "Data Source=Something";

1
你可以将它放在资源文件中。它不会具有ConfigurationManager类的内置功能,但它可以工作。
假设Resources.resx:
Resources.Default.ConnectionString = "Server=myserver;" // etc
然后在您的代码中:
conn.ConnectionString = Resources.Default.ConnectionString
我知道这是一个hack。

1
除了其他给出的答案之外,假设连接字符串不仅仅是另一个配置变量或常数作为整体,您可能考虑使用SqlConnectionStringBuilder类而不是直接将字符串连接在一起。
编辑:哎呀,抱歉刚看到您基本上想从另一个来源读取连接字符串(完整的我猜)。

-1

ConfigurationManager 用于从配置文件中读取

您的解决方案是将 conn.ConnectionString 设置为所需的连接字符串。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接