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

问题描述 投票:0回答:4

我喜欢在运行时更新

AppSettings
Web.config
部分中定义的键/值。但是我不想将它们实际保存到
Web.config
文件中。

我有一个巨大的 Web 应用程序,其中包含许多模块、DLL 和源代码文件。数据库配置、加密密钥、Web 服务的用户名和密码等一系列关键信息都保存在

AppSettings
文件的
web.config
部分中。最近的项目需求需要我将这些值移出
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
读取其设置是不可行的,另一方面,我不应该将这些值保存在
web.config
文件中可供网络管理员和操作员、系统管理员和云管理员使用。

为了让程序员的生活更轻松,我想让他们在开发过程中将其值添加到

AppSettings
web.config
部分,但它们将从那里删除并稍后在部署期间放入安全存储中,但是这些值应该可供程序透明,因为它们仍在
AppSettings
部分。

问题:如何在运行时向

AppSettings
添加值,以便程序可以使用
ConfigurationManager.AppSettings["ACriticalKey"]
读取它们来获取
"VeryCriticalValue"
,而不将它们保存在 Web.Config 中?

请注意

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

请注意:最好某些设置应该能够像以前一样保留在

AppSettings

c# .net web-config asp.net-4.0
4个回答
16
投票

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

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

7
投票

您需要利用

WebConfigurationManager.OpenWebConfiguration()

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

1
投票

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

此外,关于相同/相似主题的SO问题here可能会引起兴趣。

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


0
投票

感谢 nkvu 将我引导到他的第一个链接,该链接又将我发送到Williarob的帖子“覆盖配置管理器”,我设法找到了我的问题的解决方案。

提到的博客文章介绍了如何从另一个 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

    //...

}
© www.soinside.com 2019 - 2024. All rights reserved.