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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15653621/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 17:29:13  来源:igfitidea点击:

How to Update (Add/Modify/Delete) keys in AppSettings section of web.config at runtime

c#web-configasp.net-4.0

提问by AaA

I like to Update keys/Values defined in AppSettingssection of Web.configat runtime. however I DO NOT want to actually save them to Web.configfile.

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

I have a huge web application that have consists of many modules, DLLs and source code files. A bunch of critical information ranged from database configuration, encryption keys, username and passwords for webservices are saved in AppSettingssection of the web.configfile. Recent project requirement needs me to move these values out of web.configand keep in a secure storage.

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

I already secured these values in an external location and I can read them back when application starts.

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

here is the sample code.

这是示例代码。

Global.asax

全球.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);
        }
    }
}

As you may noticed it is not feasible to change references to AppSettingsin a massive code created by multiple programming teams to read their settings from my secureConfig dictionaryand on the other hand I should not save these values in web.configfile which is available to web administrators and operators, system admins and cloud admins.

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

To Make programmers life easier, I want to let them add their values to AppSettingssection of web.configduring development, but they will be removed from there and put to secure storage later during deployment, however these values should be available to program transparently as they are still in AppSettingssection.

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

Question: how can I add values to AppSettingsat runtime so program can read them using ConfigurationManager.AppSettings["ACriticalKey"]to get "VeryCriticalValue"without saving them in Web.Config?

:我怎么能添加值AppSettings在运行时使程序可以读取他们用ConfigurationManager.AppSettings["ACriticalKey"]得到"VeryCriticalValue",但不保存他们的Web.Config?

Please note: ConfigurationManager.AppSettings.Add(item.Key, item.Value);gives me ConfigurationErrorsExceptionwith message The configuration is read only.

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

Please note: Preferably some settings should be able to stay in AppSettingsas before

请注意:最好一些设置应该能够保持AppSettings原样

采纳答案by AaA

Thanks to nkvu which directed me to a his first link which in turn sent me to Williarob's post "Override Configuration Manager" I managed to find a solution to my question.

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

The mentioned blog post covers how to read settings from another XML file and it works with both windowed applications and web applications (with a little modification in config file name and path). Although this blog written on 2010 it is still working fine with .NET4 without problem.

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

However as I was going to read my configuration from a secure device, I simplified the class and here is how to use the classes provided by Williarob

然而,当我要从安全设备读取我的配置时,我简化了类,这里是如何使用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
    }
}

To install this (or original version of configuration override) add following line to your Global. class (Global.asax.cs) in Application_Start

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

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

like below:

像下面这样:

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

    //...

}

回答by nkvu

Perhaps thislink will help. It references 2.0 but I believe the method is still valid in 4.0.

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

Also, the SO question on the same/similar topic heremay be of interest.

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

Also, modifying the web.config at runtime should cause an application pool recycle each time. Not trying to tell you how to suck eggs, just thought I'd note it for anyone's prospective interest...Thx.

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

回答by Rajesh Subramanian

You need to make use of WebConfigurationManager.OpenWebConfiguration()

你需要利用 WebConfigurationManager.OpenWebConfiguration()

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

回答by Sergio Flores

I know this is an old question, but I ran into the same problem and I found that Set works in the same way as Add, and does not throw an exception, so just replace Add with Set, like so:

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

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