【问题标题】:How to Update (Add/Modify/Delete) keys in AppSettings section of web.config at runtime如何在运行时更新(添加/修改/删除)web.config 的 AppSettings 部分中的键
【发布时间】:2013-03-17 05:24:35
【问题描述】:

我喜欢在运行时更新在Web.configAppSettings 部分中定义的键/值。但是我不想将它们实际保存到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<string, string> secureConfig = new Dictionary<string,string>{};

        // --------------------------------------------------------------------
        // 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<string, string> item in secureConfig) {
            ConfigurationManager.AppSettings.Add(item.Key, item.Value);
        }
    }
}

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

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

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

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

请注意:一些设置最好能像以前一样保留在AppSettings

【问题讨论】:

标签: c# web-config asp.net-4.0


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    你需要使用WebConfigurationManager.OpenWebConfiguration()

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

    【讨论】:

    • 感谢您的回答,但我不想将值保存回 Web.Config 文件。另一方面,使用 IIS 设置,此代码也将无法工作,因为 Web.Config 是只读的,除非您更改共享服务器中不允许的 Machine.config
    【解决方案3】:

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

    另外,关于相同/相似主题的 SO 问题here 可能很有趣。

    此外,在运行时修改 web.config 每次都会导致应用程序池回收。不是想告诉你如何吸鸡蛋,只是想我会为了任何人的潜在兴趣而记下它......谢谢。

    【讨论】:

    • 感谢您的回答,但是这里建议的文章也将值保存回配置文件(除非您修改 machine.config,否则由于 IIS 的保护级别,这是不可能的)。你是对的,修改Web.config 回收应用程序池,这也不是预期的。
    • AFAIK 我不认为你可以。我认为唯一的方法是通过创建一个带有 AppSettings 属性的 SecureConfigurationManager 类来添加另一层indirection,该属性可以像 ConfigurationManager 的 AppSettings(例如 SecureConfigurationManager.AppSettings[])一样被调用,但其中有逻辑可以先检查你的 Dictionary 然后再下降如果没有找到它,请返回ConfigurationManager.AppSettings[](反之亦然)。我注意到这是一个代码更改(不是您所追求的),但可能影响不大
    • 这意味着您的开发人员不会注意到设置是否来自 web.config 中的appSettings,并且仍然可以愉快地将东西放在那里,并且您的包装器会在生产时考虑它当设置不存在时。但是,我可能已经跑题了,所以如果这不是你想要的,请反馈给我——如果不是,我会删除这个答案,因为它并没有真正为你的问题添加任何东西。抱歉,我无法提供更多帮助。
    • 其实你的回答很有帮助。您分享的第一个链接参考了另一篇关于如何修改ConfigurationManager 类以从其他地方读取设置的文章。我现在正在研究那篇文章,如果它成功了,我会在这里发布我的发现。
    【解决方案4】:

    感谢 nkvu 将我引导至他的第一个链接,该链接又将我发送至 Williarob 的帖子“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;
    
            /// <summary>
            /// Re-initializes the ConfigurationManager, allowing us to merge in the settings from Core.Config
            /// </summary>
            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<string, string> 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
        }
    }
    

    要安装此(或配置覆盖的原始版本),请将以下行添加到 你的全球。 Application_Start 中的类 (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
    
        //...
    
    }
    

    【讨论】:

      猜你喜欢
      • 2010-10-17
      • 1970-01-01
      • 2014-07-07
      • 2012-06-17
      • 1970-01-01
      • 2011-08-26
      • 2011-12-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多