簡體   English   中英

如何在運行時更新(添加/修改/刪除)web.config的AppSettings部分中的鍵

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

我喜歡在運行時更新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<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讀取其設置是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相同,並且不會拋出異常,所以只需將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的帖子“ 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
    }
}

要安裝此(或配置覆蓋的原始版本),請將以下行添加到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

    //...

}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM