如何在运行时更新(添加/修改/删除)web.config的AppSettings部分中的键

我喜欢在运行时更新Web.config AppSettings部分中定义的键/值。 但是我不想将它们实际保存到Web.config文件中。

我有一个巨大的Web应用程序,包含许多模块,DLL和源代码文件。 一堆关键信息包括数据库配置,加密密钥,用户名和Web服务密码,都保存在web.config文件的AppSettings部分。 最近的项目要求需要我将这些值从web.config移出并保存在安全存储中。

我已经在外部位置保护了这些值,我可以在应用程序启动时读回它们。

这是示例代码。

Global.asax中

 public class Global: System.Web.HttpApplication { protected void Application_Start(object sender, EventArgs e) { Dictionary secureConfig = new Dictionary{}; // -------------------------------------------------------------------- // Here I read and decrypt keys and add them to secureConfig dictionary // To test assume the following line is a key stored in secure sotrage. //secureConfig = SecureConfig.LoadConfig(); secureConfig.Add("ACriticalKey","VeryCriticalValue"); // -------------------------------------------------------------------- foreach (KeyValuePair item in secureConfig) { ConfigurationManager.AppSettings.Add(item.Key, item.Value); } } } 

您可能已经注意到,在由多个编程团队创建的大量代码中更改对AppSettings引用以从我的secureConfig dictionary读取其设置是secureConfig dictionary ,另一方面,我不应将这些值保存在web.config文件中,该文件可用于Web管理员和操作员,系统管理员和云管理员。

为了让程序员的生活更轻松,我想让他们在开发过程中将他们的值添加到web.config AppSettings部分,但是他们将从那里删除并在部署期间放入安全存储,但是这些值应该可以透明地编程为它们仍然在AppSettings部分。

问题 :如何在运行时向AppSettings添加值,以便程序可以使用ConfigurationManager.AppSettings["ACriticalKey"]读取它们以获取"VeryCriticalValue"而不将它们保存在Web.Config中?

请注意ConfigurationManager.AppSettings.Add(item.Key, item.Value); 给我带有消息The configuration is read only. ConfigurationErrorsException The configuration is read only.

请注意 :某些设置应该能够像之前一样保留在AppSettings

我知道这是一个老问题,但我遇到了同样的问题,我发现Set的工作方式与Add相同,并且不会抛出exception,所以只需将Add替换为Set,就像这样:

 ConfigurationManager.AppSettings.Set(item.Key, item.Value); 

您需要使用WebConfigurationManager.OpenWebConfiguration()

 Configuration config = WebConfigurationManager.OpenWebConfiguration(HttpContext.Current.Request.ApplicationPath); config.AppSettings.Settings.Remove("Variable"); config.AppSettings.Settings.Add("Variable", "valyue"); config.Save(); 

也许这个链接会有所帮助。 它引用了2.0,但我相信该方法在4.0中仍然有效。

此外,关于相同/类似主题的SO问题可能是有意义的。

此外,在运行时修改web.config应该每次都会导致应用程序池回收。 不要试图告诉你如何吸蛋,只是想我会注意到任何人的预期兴趣… Thx。

感谢nkvu指导我到他的第一个链接,然后将我发送到Williarob的post“ Override Configuration Manager ”,我设法找到了我的问题的解决方案。

上面提到的博客文章介绍了如何从另一个XML文件中读取设置,它适用于窗口化应用程序和Web应用程序(在配置文件名和路径中稍作修改)。 虽然这篇博客是在2010年写的,但它仍然可以正常使用.NET4而没有问题。

但是,当我要从安全设备读取配置时,我简化了类,这里是如何使用Williarob提供的类

 using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Configuration; using System.Configuration.Internal; using System.Linq; using System.Reflection; namespace Williablog.Core.Configuration { public sealed class ConfigSystem: IInternalConfigSystem { private static IInternalConfigSystem clientConfigSystem; private object appsettings; private object connectionStrings; ///  /// Re-initializes the ConfigurationManager, allowing us to merge in the settings from Core.Config ///  public static void Install() { FieldInfo[] fiStateValues = null; Type tInitState = typeof(System.Configuration.ConfigurationManager).GetNestedType("InitState", BindingFlags.NonPublic); if (null != tInitState) { fiStateValues = tInitState.GetFields(); } FieldInfo fiInit = typeof(System.Configuration.ConfigurationManager).GetField("s_initState", BindingFlags.NonPublic | BindingFlags.Static); FieldInfo fiSystem = typeof(System.Configuration.ConfigurationManager).GetField("s_configSystem", BindingFlags.NonPublic | BindingFlags.Static); if (fiInit != null && fiSystem != null && null != fiStateValues) { fiInit.SetValue(null, fiStateValues[1].GetValue(null)); fiSystem.SetValue(null, null); } ConfigSystem confSys = new ConfigSystem(); Type configFactoryType = Type.GetType("System.Configuration.Internal.InternalConfigSettingsFactory, System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", true); IInternalConfigSettingsFactory configSettingsFactory = (IInternalConfigSettingsFactory) Activator.CreateInstance(configFactoryType, true); configSettingsFactory.SetConfigurationSystem(confSys, false); Type clientConfigSystemType = Type.GetType("System.Configuration.ClientConfigurationSystem, System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", true); clientConfigSystem = (IInternalConfigSystem) Activator.CreateInstance(clientConfigSystemType, true); } #region IInternalConfigSystem Members public object GetSection(string configKey) { // get the section from the default location (web.config or app.config) object section = clientConfigSystem.GetSection(configKey); switch (configKey) { case "appSettings": // Return cached version if exists if (this.appsettings != null) { return this.appsettings; } // create a new collection because the underlying collection is read-only var cfg = new NameValueCollection(); // If an AppSettings section exists in Web.config, read and add values from it if (section is NameValueCollection) { NameValueCollection localSettings = (NameValueCollection) section; foreach (string key in localSettings) { cfg.Add(key, localSettings[key]); } } // -------------------------------------------------------------------- // Here I read and decrypt keys and add them to secureConfig dictionary // To test assume the following line is a key stored in secure sotrage. //secureConfig = SecureConfig.LoadConfig(); secureConfig.Add("ACriticalKey", "VeryCriticalValue"); // -------------------------------------------------------------------- foreach (KeyValuePair item in secureConfig) { if (cfg.AllKeys.Contains(item.Key)) { cfg[item.Key] = item.Value; } else { cfg.Add(item.Key, item.Value); } } // -------------------------------------------------------------------- // Cach the settings for future use this.appsettings = cfg; // return the merged version of the items from secure storage and appsettings section = this.appsettings; break; case "connectionStrings": // Return cached version if exists if (this.connectionStrings != null) { return this.connectionStrings; } // create a new collection because the underlying collection is read-only ConnectionStringsSection connectionStringsSection = new ConnectionStringsSection(); // copy the existing connection strings into the new collection foreach (ConnectionStringSettings connectionStringSetting in ((ConnectionStringsSection) section).ConnectionStrings) { connectionStringsSection.ConnectionStrings.Add(connectionStringSetting); } // -------------------------------------------------------------------- // Again Load connection strings from secure storage and merge like below // connectionStringsSection.ConnectionStrings.Add(connectionStringSetting); // -------------------------------------------------------------------- // Cach the settings for future use this.connectionStrings = connectionStringsSection; // return the merged version of the items from secure storage and appsettings section = this.connectionStrings; break; } return section; } public void RefreshConfig(string sectionName) { if (sectionName == "appSettings") { this.appsettings = null; } if (sectionName == "connectionStrings") { this.connectionStrings = null; } clientConfigSystem.RefreshConfig(sectionName); } public bool SupportsUserConfig { get { return clientConfigSystem.SupportsUserConfig; } } #endregion } } 

要安装此(或配置覆盖的原始版本),请将以下行添加到Global。 Application_Start中的class(Global.asax.cs)

 Williablog.Core.Configuration.ConfigSystem .Install(); 

如下:

 public class Global: System.Web.HttpApplication { //... #region protected void Application_Start(...) protected void Application_Start(object sender, EventArgs e) { Williablog.Core.Configuration.ConfigSystem .Install(); //... } #endregion //... }