C# 程序修改config文件後,不重啓程序刷新配置ConfigurationManager

基本共識:html

ConfigurationManager 自帶緩存,且不支持 寫入。node

若是 經過 文本寫入方式 修改 配置文件,程序 沒法刷新加載 最新配置。緩存

PS. Web.config 除外:Web.config 修改後,網站會重啓 (即 Web 程序 也沒法在 運行時 刷新配置)。多線程

 

爲何要在程序運行時,修改配置(刷新配置):併發

> 之前C++,VB 時代,用戶在程序界面 勾選的配置,會寫到 ini 文件。app

> C# 自帶 .exe.config 配置文件 —— 可是,C# 自帶的 ConfigurationManager 不支持 運行時 修改,運行時刷新配置。高併發

> 本文 提供工具類,完全 解決 這個問題 —— 今後,用戶手動勾選的配置 不再用寫入 ini,而是直接修改 .exe.config 文件,且當即刷新。工具

 

刷新 ConfigurationManager 配置 的 代碼 有兩種:網站

> 第一種:this

ConfigurationManager.RefreshSection("appSettings");        //刷新 appSettings 節點 (當即生效)
ConfigurationManager.RefreshSection("connectionString");   //刷新 connectionString 節點 (沒法生效 —— 多是 微軟處理時,由於 LocalSqlServer 這個默認配置 而致使的疏忽)

> 第二種:

FieldInfo fieldInfo = typeof(ConfigurationManager).GetField("s_initState", BindingFlags.NonPublic | BindingFlags.Static);
if (fieldInfo != null) fieldInfo.SetValue(null, 0); //將配置文件 設置爲: 未分析 狀態, 配置文件 將會在下次讀取 時 從新分析.
//當即生效,並且效果 明顯 —— 就喜歡這種 暴力作法。

 

一塊兒反編譯 ConfigurationManager 代碼:

> 首先 下載 ILSpy 或 Reflector (本文使用的是 ILSpy.)

> 打開 ILSpy 搜索 ConfigurationManager,執行以下操做:

 

> 編寫 反射代碼,刷新 配置文件數據。(具體代碼 在 文章最開始。)

 

額外提供 配置文件 修改的 工具類代碼:

如下代碼 實現以下功能:

> 執行 配置寫入操做時,自動建立 .exe.config 文件,自動建立 appSettings  connectionString 節點。

> .exe.config 寫入配置時,若是 相同的 key  name 存在,則修改,不存在 則建立。

> 額外的 審美操做

     > 不少人習慣 appSettings 顯示在 connectionString 前面。

     > 不少人習慣 appSettings 在 最前面。

     > appSettings 必須在 configSections 後面。(configSections 配置文件 擴展配置節點,只能寫在第一個,不然 程序報錯。)

using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
using System.Xml;

namespace InkFx.Utils
{
    public partial class Tools
    {

        private static ConfigAppSetting m_AppSettings;
        private static ConfigConnectionStrings m_ConnectionStrings;

        public static ConfigAppSetting AppSettings
        {
            get
            {
                if (m_AppSettings == null)
                {
                    m_AppSettings = new ConfigAppSetting();
                    m_AppSettings.AppSettingChanged += OnAppSettingChanged;
                }
                return m_AppSettings;
            }
        }
        public static ConfigConnectionStrings ConnectionStrings
        {
            get
            {
                if (m_ConnectionStrings == null)
                {
                    m_ConnectionStrings = new ConfigConnectionStrings();
                    m_ConnectionStrings.ConnectionStringsChanged += OnConnectionStringsChanged;
                }
                return m_ConnectionStrings;
            }
        }



        private static void OnAppSettingChanged(string name, string value)
        {
            string configPath = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
            if (!File.Exists(configPath))
            {
                const string content = @"<?xml version=""1.0""?><configuration></configuration>";
                File.WriteAllText(configPath, content, Encoding.UTF8);
            }

            XmlDocument doc = new XmlDocument();
            doc.Load(configPath);

            XmlNode nodeConfiguration = doc.SelectSingleNode(@"configuration");
            if (nodeConfiguration == null)
            {
                nodeConfiguration = doc.CreateNode(XmlNodeType.Element, "configuration", string.Empty);
                doc.AppendChild(nodeConfiguration);
            }

            XmlNode nodeAppSettings = nodeConfiguration.SelectSingleNode(@"appSettings");
            if (nodeAppSettings == null)
            {
                nodeAppSettings = doc.CreateNode(XmlNodeType.Element, "appSettings", string.Empty);
                if (!nodeConfiguration.HasChildNodes)
                    nodeConfiguration.AppendChild(nodeAppSettings);
                else
                {
                    //configSections 必須放在 第一個, 因此得 避開 configSections
                    XmlNode firstNode = nodeConfiguration.ChildNodes[0];
                    bool firstNodeIsSections = string.Equals(firstNode.Name, "configSections", StringComparison.CurrentCultureIgnoreCase);

                    if (firstNodeIsSections)
                        nodeConfiguration.InsertAfter(nodeAppSettings, firstNode);
                    else
                        nodeConfiguration.InsertBefore(nodeAppSettings, firstNode);
                }
            }

            string xmlName = FormatXmlStr(name);
            XmlNode nodeAdd = nodeAppSettings.SelectSingleNode(@"add[@key='" + xmlName + "']");
            if (nodeAdd == null)
            {
                nodeAdd = doc.CreateNode(XmlNodeType.Element, "add", string.Empty);
                nodeAppSettings.AppendChild(nodeAdd);
            }

            XmlElement nodeElem = (XmlElement)nodeAdd;
            nodeElem.SetAttribute("key", name);
            nodeElem.SetAttribute("value", value);
            doc.Save(configPath);

            try { ConfigurationManager.RefreshSection("appSettings"); } catch (Exception) { }
        }
        private static void OnConnectionStringsChanged(string name, string value)
        {
            string configPath = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
            if (!File.Exists(configPath))
            {
                const string content = @"<?xml version=""1.0""?><configuration></configuration>";
                File.WriteAllText(configPath, content, Encoding.UTF8);
            }

            XmlDocument doc = new XmlDocument();
            doc.Load(configPath);

            XmlNode nodeConfiguration = doc.SelectSingleNode(@"configuration");
            if (nodeConfiguration == null)
            {
                nodeConfiguration = doc.CreateNode(XmlNodeType.Element, "configuration", string.Empty);
                doc.AppendChild(nodeConfiguration);
            }

            XmlNode nodeAppSettings = nodeConfiguration.SelectSingleNode(@"appSettings");
            XmlNode nodeConnectionStrings = nodeConfiguration.SelectSingleNode(@"connectionStrings");
            if (nodeConnectionStrings == null)
            {
                nodeConnectionStrings = doc.CreateNode(XmlNodeType.Element, "connectionStrings", string.Empty);
                if (!nodeConfiguration.HasChildNodes)
                    nodeConfiguration.AppendChild(nodeConnectionStrings);
                else
                {
                    //優先將 connectionStrings 放在 appSettings 後面
                    if (nodeAppSettings != null)
                        nodeConfiguration.InsertAfter(nodeConnectionStrings, nodeAppSettings);
                    else
                    {
                        //若是 沒有 appSettings 節點, 則 configSections 必須放在 第一個, 因此得 避開 configSections
                        XmlNode firstNode = nodeConfiguration.ChildNodes[0];
                        bool firstNodeIsSections = string.Equals(firstNode.Name, "configSections", StringComparison.CurrentCultureIgnoreCase);

                        if (firstNodeIsSections)
                            nodeConfiguration.InsertAfter(nodeConnectionStrings, firstNode);
                        else
                            nodeConfiguration.InsertBefore(nodeConnectionStrings, firstNode);
                    }
                }
            }

            string xmlName = FormatXmlStr(name);
            XmlNode nodeAdd = nodeConnectionStrings.SelectSingleNode(@"add[@name='" + xmlName + "']");
            if (nodeAdd == null)
            {
                nodeAdd = doc.CreateNode(XmlNodeType.Element, "add", string.Empty);
                nodeConnectionStrings.AppendChild(nodeAdd);
            }

            XmlElement nodeElem = (XmlElement)nodeAdd;
            nodeElem.SetAttribute("name", name);
            nodeElem.SetAttribute("connectionString", value);
            doc.Save(configPath);

            try
            {
                ConfigurationManager.RefreshSection("connectionString");  //RefreshSection 沒法刷新 connectionString 節點
                FieldInfo fieldInfo = typeof(ConfigurationManager).GetField("s_initState", BindingFlags.NonPublic | BindingFlags.Static);
                if (fieldInfo != null) fieldInfo.SetValue(null, 0);       //將配置文件 設置爲: 未分析 狀態, 配置文件 將會在下次讀取 時 從新分析.
            }
            catch (Exception) { }
        }

        private static string FormatXmlStr(string value)
        {
            if (string.IsNullOrEmpty(value)) return string.Empty;

            string result = value
                .Replace("<", "&lt;")
                .Replace(">", "&gt;")
                .Replace("&", "&amp;")
                .Replace("'", "&apos;")
                .Replace("\"", "&quot;");
            return result;
//&lt; < 小於號 
//&gt; > 大於號 
//&amp; & 和 
//&apos; ' 單引號 
//&quot; " 雙引號 
        }


        public class ConfigAppSetting
        {
            private readonly InnerIgnoreDict<string> m_Hash = new InnerIgnoreDict<string>();

            public string this[string name]
            {
                get
                {
                    string value = m_Hash[name];
                    if (string.IsNullOrWhiteSpace(value))
                    {
                        try { value = ConfigurationManager.AppSettings[name]; } catch(Exception) { }
                        m_Hash[name] = value;
                        return value;
                    }
                    return value;
                }
                set
                {
                    m_Hash[name] = value;
                    try{ ConfigurationManager.AppSettings[name] = value; } catch(Exception) { }
                    if (AppSettingChanged != null) AppSettingChanged(name, value);
                }
            }
            public AppSettingValueChanged AppSettingChanged;

            public delegate void AppSettingValueChanged(string name, string value);
        }
        public class ConfigConnectionStrings
        {
            private readonly InnerIgnoreDict<ConnectionStringSettings> m_Hash = new InnerIgnoreDict<ConnectionStringSettings>();

            public string this[string name]
            {
                get
                {
                    ConnectionStringSettings value = m_Hash[name];
                    if (value == null || string.IsNullOrWhiteSpace(value.ConnectionString))
                    {
                        try { value = ConfigurationManager.ConnectionStrings[name]; } catch (Exception) { }
                        m_Hash[name] = value;
                        return value == null ? string.Empty : value.ConnectionString;
                    }
                    return value.ConnectionString;
                }
                set
                {

                    ConnectionStringSettings setting = new ConnectionStringSettings();
                    setting.Name = name;
                    setting.ConnectionString = value;
                    m_Hash[name] = setting;
                    //try { ConfigurationManager.ConnectionStrings[name] = setting; } catch (Exception) { }
                    if (ConnectionStringsChanged != null) ConnectionStringsChanged(name, value);
                }
            }
            public ConnectionStringsValueChanged ConnectionStringsChanged;

            public delegate void ConnectionStringsValueChanged(string name, string value);
        }



        private class InnerIgnoreDict<T> : Dictionary<string, T>
        {
            public InnerIgnoreDict(): base(StringComparer.CurrentCultureIgnoreCase)
            {
            }

#if (!WindowsCE && !PocketPC)
            public InnerIgnoreDict(SerializationInfo info, StreamingContext context) : base(info, context) { }
#endif

            private readonly object getSetLocker = new object();
            private static readonly T defaultValue = default(T);

            public new T this[string key]
            {
                get
                {
                    if (key == null) return defaultValue;
                    lock (getSetLocker) //爲了 多線程的 高併發, 取值也 加上 線程鎖
                    {
                        T record;
                        if (TryGetValue(key, out record)) return record;
                        else return defaultValue;
                    }
                }
                set
                {
                    try
                    {
                        if (key != null)
                        {
                            lock (getSetLocker)
                            {
                                //if (!value.Equals(default(T)))
                                //{
                                if (base.ContainsKey(key)) base[key] = value;
                                else base.Add(key, value);
                                //}
                                //else
                                //{
                                //    base.Remove(key);
                                //}
                            }
                        }
                    }
                    catch (Exception) { }
                }
            }
        }

    }
}

工具類使用代碼:

static void Main(string[] args)
        {
            Tools.AppSettings["Test"] = "Love";                           //修改配置文件
            Console.WriteLine(ConfigurationManager.AppSettings["Test"]);  //傳統方式 讀取配置文件
            Console.WriteLine(Tools.AppSettings["Test"]);                 //工具類 讀取配置文件

            Tools.ConnectionStrings["ConnString"] = "Data Source=127.0.0.1;Initial Catalog=master;User=sa;password=123.com;";
            Console.WriteLine(ConfigurationManager.ConnectionStrings["ConnString"]);
            Console.WriteLine(Tools.ConnectionStrings["ConnString"]);

            Tools.AppSettings["Test"] = "<Love>";
            Console.WriteLine(ConfigurationManager.AppSettings["Test"]);
            Console.WriteLine(Tools.AppSettings["Test"]);

            Console.ReadKey();
        }

 

執行結果:

 

配置文件變化:

> 程序執行前,刪除配置文件。

> 程序執行後,自動生成配置文件。

 

出處:https://www.cnblogs.com/shuxiaolong/p/20160907_1432.html

完畢!

相關文章
相關標籤/搜索