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

摘要:
如果通过文本写入方式修改配置文件,程序无法刷新加载最新配置。PS.Web.config除外:Web.config修改后,网站会重启。˃C#自带.exe.config配置文件——但是,C#自带的ConfigurationManager不支持运行时修改,运行时刷新配置。一起反编译ConfigurationManager代码:˃首先下载ILSpy或Reflector˃打开ILSpy搜索ConfigurationManager,执行如下操作:˃编写反射代码,刷新配置文件数据。)额外提供配置文件修改的工具类代码:以下代码实现如下功能:˃执行配置写入操作时,自动创建.exe.config文件,自动创建appSettingsconnectionString节点。˃appSettings必须在configSections后面。

基本共识:

ConfigurationManager 自带缓存,且不支持 写入。

如果 通过 文本写入方式 修改 配置文件,程序 无法刷新加载 最新配置。

PS. Web.config 除外:Web.config 修改后,网站会重启 (即 Web 程序 也无法在 运行时 刷新配置)。

为什么要在程序运行时,修改配置(刷新配置):

> 以前C++,VB 时代,用户在程序界面 勾选的配置,会写到 ini 文件。

> C# 自带 .exe.config 配置文件 —— 但是,C# 自带的 ConfigurationManager 不支持 运行时 修改,运行时刷新配置。

> 本文 提供工具类,彻底 解决 这个问题 —— 从此,用户手动勾选的配置 再也不用写入 ini,而是直接修改 .exe.config 文件,且立即刷新。

刷新 ConfigurationManager 配置 的 代码 有两种:

> 第一种:

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,执行如下操作:

C# 程序修改config文件后,不重启程序刷新配置ConfigurationManager第1张

C# 程序修改config文件后,不重启程序刷新配置ConfigurationManager第2张C# 程序修改config文件后,不重启程序刷新配置ConfigurationManager第3张

C# 程序修改config文件后,不重启程序刷新配置ConfigurationManager第4张

C# 程序修改config文件后,不重启程序刷新配置ConfigurationManager第5张

> 编写 反射代码,刷新 配置文件数据。(具体代码 在 文章最开始。)

额外提供 配置文件 修改的 工具类代码:

以下代码 实现如下功能:

> 执行 配置写入操作时,自动创建 .exe.config 文件,自动创建 appSettingsconnectionString 节点。

> .exe.config 写入配置时,如果 相同的 key name 存在,则修改,不存在 则创建。

> 额外的审美操作

> 很多人习惯appSettings 显示在connectionString 前面。

> 很多人习惯 appSettings 在 最前面。

>appSettings 必须在configSections 后面。(configSections配置文件 扩展配置节点,只能写在第一个,否则 程序报错。)

usingSystem;
usingSystem.Collections.Generic;
usingSystem.Configuration;
usingSystem.IO;
usingSystem.Reflection;
usingSystem.Runtime.Serialization;
usingSystem.Text;
usingSystem.Xml;

namespaceInkFx.Utils
{
    public partial classTools
    {

        private staticConfigAppSetting m_AppSettings;
        private staticConfigConnectionStrings m_ConnectionStrings;

        public staticConfigAppSetting AppSettings
        {
            get{
                if (m_AppSettings == null)
                {
                    m_AppSettings = newConfigAppSetting();
                    m_AppSettings.AppSettingChanged +=OnAppSettingChanged;
                }
                returnm_AppSettings;
            }
        }
        public staticConfigConnectionStrings ConnectionStrings
        {
            get{
                if (m_ConnectionStrings == null)
                {
                    m_ConnectionStrings = newConfigConnectionStrings();
                    m_ConnectionStrings.ConnectionStringsChanged +=OnConnectionStringsChanged;
                }
                returnm_ConnectionStrings;
            }
        }



        private static void OnAppSettingChanged(string name, stringvalue)
        {
            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 = newXmlDocument();
            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);
                    elsenodeConfiguration.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, stringvalue)
        {
            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 = newXmlDocument();
            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);
                        elsenodeConfiguration.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(stringvalue)
        {
            if (string.IsNullOrEmpty(value)) return string.Empty;

            string result =value
                .Replace("<", "&lt;")
                .Replace(">", "&gt;")
                .Replace("&", "&amp;")
                .Replace("'", "&apos;")
                .Replace(""", "&quot;");
            returnresult;
//&lt; < 小于号 
//&gt; > 大于号 
//&amp; & 和 
//&apos; ' 单引号 
//&quot; " 双引号 
}


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

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

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

            public string this[stringname]
            {
                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;
                    }
                    returnvalue.ConnectionString;
                }
                set{

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

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



        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[stringkey]
            {
                get{
                    if (key == null) returndefaultValue;
                    lock (getSetLocker) //为了 多线程的 高并发, 取值也 加上 线程锁
{
                        T record;
                        if (TryGetValue(key, out record)) returnrecord;
                        else returndefaultValue;
                    }
                }
                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();
        }

执行结果:

C# 程序修改config文件后,不重启程序刷新配置ConfigurationManager第6张

配置文件变化:

> 程序执行前,删除配置文件。

> 程序执行后,自动生成配置文件。

C# 程序修改config文件后,不重启程序刷新配置ConfigurationManager第7张

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

完毕!

免责声明:文章转载自《C# 程序修改config文件后,不重启程序刷新配置ConfigurationManager》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇CSPNetadb shell 查看系统属性(用来判断特殊的操作系统)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Apache Beam实战指南 | 大数据管道(pipeline)设计及实践

Apache Beam实战指南 | 大数据管道(pipeline)设计及实践  mp.weixin.qq.com 策划 & 审校 | Natalie作者 | 张海涛编辑 | LindaAI 前线导读: 本文是 Apache Beam 实战指南系列文章第五篇内容,将对 Beam 框架中的 pipeline 管道进行剖析,并结合应用示例介绍如...

C#中文和UNICODE编码转换

C#中文和UNICODE编码转换 //中文轉為UNICODE string str = "中文"; string outStr = ""; if (!string.IsNullOrEmpty(str)) { for (int i = 0; i < str.Length; i++) { //將中文轉為10進制整數,然後轉為16進制unicode out...

re库的使用

一、re库的调用 import re 二、正则表达式类型 raw string类型(原生字符串类型,即不包括转义符类型):r'text' string类型,更繁琐。 三、Re库主要功能函数 函数              说明 re.search()        在一个字符串中搜索匹配正则表达式的第一个位置,然后返回match对象 re.match(...

#应用openxml读写excel代码

这个例子比较简单,没有考虑格式之类的问题。 using DocumentFormat.OpenXml.Packaging; using DocumentFormat.OpenXml.Spreadsheet; using System; using System.Collections.Generic; using System.IO; using Syst...

Go:创建新进程(os.StartProcess源码解读)

关于如何使用go语言实现新进程的创建和进程间通信,我在网上找了不少的资料,但是始终未能发现让自己满意的答案,因此我打算自己来分析这部分源代码,然后善加利用,并且分享给大家,期望大家能从中获得启发。 首先我们来看一段代码 proc, _ := os.StartProcess(name, args, attr) if err != nil { fmt.Prin...

Android之Retrofit详解(转载)

说明:该文章转载于https://www.jianshu.com/p/a3e162261ab6 前言 在Andrroid开发中,网络请求十分常用 而在Android网络请求库中,Retrofit是当下最热的一个网络请求库 image 今天,我将献上一份非常详细Retrofit v2.0的使用教程,希望你们会喜欢。 如果对Retrofit v2.0...