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

Stella981
• 阅读 556

基本共识:

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

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

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

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

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

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

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

以下代码 实现如下功能:

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

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

> 额外的 审美操作

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

很多人习惯 appSettings 在 最前面。

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

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

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Configuration;
  4 using System.IO;
  5 using System.Reflection;
  6 using System.Runtime.Serialization;
  7 using System.Text;
  8 using System.Xml;
  9 
 10 namespace InkFx.Utils
 11 {
 12     public partial class Tools
 13     {
 14 
 15         private static ConfigAppSetting m_AppSettings;
 16         private static ConfigConnectionStrings m_ConnectionStrings;
 17 
 18         public static ConfigAppSetting AppSettings
 19         {
 20             get
 21             {
 22                 if (m_AppSettings == null)
 23                 {
 24                     m_AppSettings = new ConfigAppSetting();
 25                     m_AppSettings.AppSettingChanged += OnAppSettingChanged;
 26                 }
 27                 return m_AppSettings;
 28             }
 29         }
 30         public static ConfigConnectionStrings ConnectionStrings
 31         {
 32             get
 33             {
 34                 if (m_ConnectionStrings == null)
 35                 {
 36                     m_ConnectionStrings = new ConfigConnectionStrings();
 37                     m_ConnectionStrings.ConnectionStringsChanged += OnConnectionStringsChanged;
 38                 }
 39                 return m_ConnectionStrings;
 40             }
 41         }
 42 
 43 
 44 
 45         private static void OnAppSettingChanged(string name, string value)
 46         {
 47             string configPath = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
 48             if (!File.Exists(configPath))
 49             {
 50                 const string content = @"<?xml version=""1.0""?><configuration></configuration>";
 51                 File.WriteAllText(configPath, content, Encoding.UTF8);
 52             }
 53 
 54             XmlDocument doc = new XmlDocument();
 55             doc.Load(configPath);
 56 
 57             XmlNode nodeConfiguration = doc.SelectSingleNode(@"configuration");
 58             if (nodeConfiguration == null)
 59             {
 60                 nodeConfiguration = doc.CreateNode(XmlNodeType.Element, "configuration", string.Empty);
 61                 doc.AppendChild(nodeConfiguration);
 62             }
 63 
 64             XmlNode nodeAppSettings = nodeConfiguration.SelectSingleNode(@"appSettings");
 65             if (nodeAppSettings == null)
 66             {
 67                 nodeAppSettings = doc.CreateNode(XmlNodeType.Element, "appSettings", string.Empty);
 68                 if (!nodeConfiguration.HasChildNodes)
 69                     nodeConfiguration.AppendChild(nodeAppSettings);
 70                 else
 71                 {
 72                     //configSections 必须放在 第一个, 所以得 避开 configSections
 73                     XmlNode firstNode = nodeConfiguration.ChildNodes[0];
 74                     bool firstNodeIsSections = string.Equals(firstNode.Name, "configSections", StringComparison.CurrentCultureIgnoreCase);
 75 
 76                     if (firstNodeIsSections)
 77                         nodeConfiguration.InsertAfter(nodeAppSettings, firstNode);
 78                     else
 79                         nodeConfiguration.InsertBefore(nodeAppSettings, firstNode);
 80                 }
 81             }
 82 
 83             string xmlName = FormatXmlStr(name);
 84             XmlNode nodeAdd = nodeAppSettings.SelectSingleNode(@"add[@key='" + xmlName + "']");
 85             if (nodeAdd == null)
 86             {
 87                 nodeAdd = doc.CreateNode(XmlNodeType.Element, "add", string.Empty);
 88                 nodeAppSettings.AppendChild(nodeAdd);
 89             }
 90 
 91             XmlElement nodeElem = (XmlElement)nodeAdd;
 92             nodeElem.SetAttribute("key", name);
 93             nodeElem.SetAttribute("value", value);
 94             doc.Save(configPath);
 95 
 96             try { ConfigurationManager.RefreshSection("appSettings"); } catch (Exception) { }
 97         }
 98         private static void OnConnectionStringsChanged(string name, string value)
 99         {
100             string configPath = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
101             if (!File.Exists(configPath))
102             {
103                 const string content = @"<?xml version=""1.0""?><configuration></configuration>";
104                 File.WriteAllText(configPath, content, Encoding.UTF8);
105             }
106 
107             XmlDocument doc = new XmlDocument();
108             doc.Load(configPath);
109 
110             XmlNode nodeConfiguration = doc.SelectSingleNode(@"configuration");
111             if (nodeConfiguration == null)
112             {
113                 nodeConfiguration = doc.CreateNode(XmlNodeType.Element, "configuration", string.Empty);
114                 doc.AppendChild(nodeConfiguration);
115             }
116 
117             XmlNode nodeAppSettings = nodeConfiguration.SelectSingleNode(@"appSettings");
118             XmlNode nodeConnectionStrings = nodeConfiguration.SelectSingleNode(@"connectionStrings");
119             if (nodeConnectionStrings == null)
120             {
121                 nodeConnectionStrings = doc.CreateNode(XmlNodeType.Element, "connectionStrings", string.Empty);
122                 if (!nodeConfiguration.HasChildNodes)
123                     nodeConfiguration.AppendChild(nodeConnectionStrings);
124                 else
125                 {
126                     //优先将 connectionStrings 放在 appSettings 后面
127                     if (nodeAppSettings != null)
128                         nodeConfiguration.InsertAfter(nodeConnectionStrings, nodeAppSettings);
129                     else
130                     {
131                         //如果 没有 appSettings 节点, 则 configSections 必须放在 第一个, 所以得 避开 configSections
132                         XmlNode firstNode = nodeConfiguration.ChildNodes[0];
133                         bool firstNodeIsSections = string.Equals(firstNode.Name, "configSections", StringComparison.CurrentCultureIgnoreCase);
134 
135                         if (firstNodeIsSections)
136                             nodeConfiguration.InsertAfter(nodeConnectionStrings, firstNode);
137                         else
138                             nodeConfiguration.InsertBefore(nodeConnectionStrings, firstNode);
139                     }
140                 }
141             }
142 
143             string xmlName = FormatXmlStr(name);
144             XmlNode nodeAdd = nodeConnectionStrings.SelectSingleNode(@"add[@name='" + xmlName + "']");
145             if (nodeAdd == null)
146             {
147                 nodeAdd = doc.CreateNode(XmlNodeType.Element, "add", string.Empty);
148                 nodeConnectionStrings.AppendChild(nodeAdd);
149             }
150 
151             XmlElement nodeElem = (XmlElement)nodeAdd;
152             nodeElem.SetAttribute("name", name);
153             nodeElem.SetAttribute("connectionString", value);
154             doc.Save(configPath);
155 
156             try
157             {
158                 ConfigurationManager.RefreshSection("connectionString");  //RefreshSection 无法刷新 connectionString 节点
159                 FieldInfo fieldInfo = typeof(ConfigurationManager).GetField("s_initState", BindingFlags.NonPublic | BindingFlags.Static);
160                 if (fieldInfo != null) fieldInfo.SetValue(null, 0);       //将配置文件 设置为: 未分析 状态, 配置文件 将会在下次读取 时 重新分析.
161             }
162             catch (Exception) { }
163         }
164 
165         private static string FormatXmlStr(string value)
166         {
167             if (string.IsNullOrEmpty(value)) return string.Empty;
168 
169             string result = value
170                 .Replace("<", "&lt;")
171                 .Replace(">", "&gt;")
172                 .Replace("&", "&amp;")
173                 .Replace("'", "&apos;")
174                 .Replace("\"", "&quot;");
175             return result;
176 //&lt; < 小于号 
177 //&gt; > 大于号 
178 //&amp; & 和 
179 //&apos; ' 单引号 
180 //&quot; " 双引号 
181         }
182 
183 
184         public class ConfigAppSetting
185         {
186             private readonly InnerIgnoreDict<string> m_Hash = new InnerIgnoreDict<string>();
187 
188             public string this[string name]
189             {
190                 get
191                 {
192                     string value = m_Hash[name];
193                     if (string.IsNullOrWhiteSpace(value))
194                     {
195                         try { value = ConfigurationManager.AppSettings[name]; } catch(Exception) { }
196                         m_Hash[name] = value;
197                         return value;
198                     }
199                     return value;
200                 }
201                 set
202                 {
203                     m_Hash[name] = value;
204                     try{ ConfigurationManager.AppSettings[name] = value; } catch(Exception) { }
205                     if (AppSettingChanged != null) AppSettingChanged(name, value);
206                 }
207             }
208             public AppSettingValueChanged AppSettingChanged;
209 
210             public delegate void AppSettingValueChanged(string name, string value);
211         }
212         public class ConfigConnectionStrings
213         {
214             private readonly InnerIgnoreDict<ConnectionStringSettings> m_Hash = new InnerIgnoreDict<ConnectionStringSettings>();
215 
216             public string this[string name]
217             {
218                 get
219                 {
220                     ConnectionStringSettings value = m_Hash[name];
221                     if (value == null || string.IsNullOrWhiteSpace(value.ConnectionString))
222                     {
223                         try { value = ConfigurationManager.ConnectionStrings[name]; } catch (Exception) { }
224                         m_Hash[name] = value;
225                         return value == null ? string.Empty : value.ConnectionString;
226                     }
227                     return value.ConnectionString;
228                 }
229                 set
230                 {
231 
232                     ConnectionStringSettings setting = new ConnectionStringSettings();
233                     setting.Name = name;
234                     setting.ConnectionString = value;
235                     m_Hash[name] = setting;
236                     //try { ConfigurationManager.ConnectionStrings[name] = setting; } catch (Exception) { }
237                     if (ConnectionStringsChanged != null) ConnectionStringsChanged(name, value);
238                 }
239             }
240             public ConnectionStringsValueChanged ConnectionStringsChanged;
241 
242             public delegate void ConnectionStringsValueChanged(string name, string value);
243         }
244 
245 
246 
247         private class InnerIgnoreDict<T> : Dictionary<string, T>
248         {
249             public InnerIgnoreDict(): base(StringComparer.CurrentCultureIgnoreCase)
250             {
251             }
252 
253 #if (!WindowsCE && !PocketPC)
254             public InnerIgnoreDict(SerializationInfo info, StreamingContext context) : base(info, context) { }
255 #endif
256 
257             private readonly object getSetLocker = new object();
258             private static readonly T defaultValue = default(T);
259 
260             public new T this[string key]
261             {
262                 get
263                 {
264                     if (key == null) return defaultValue;
265                     lock (getSetLocker) //为了 多线程的 高并发, 取值也 加上 线程锁
266                     {
267                         T record;
268                         if (TryGetValue(key, out record)) return record;
269                         else return defaultValue;
270                     }
271                 }
272                 set
273                 {
274                     try
275                     {
276                         if (key != null)
277                         {
278                             lock (getSetLocker)
279                             {
280                                 //if (!value.Equals(default(T)))
281                                 //{
282                                 if (base.ContainsKey(key)) base[key] = value;
283                                 else base.Add(key, value);
284                                 //}
285                                 //else
286                                 //{
287                                 //    base.Remove(key);
288                                 //}
289                             }
290                         }
291                     }
292                     catch (Exception) { }
293                 }
294             }
295         }
296 
297     }
298 }

View Code

工具类使用代码:

 1         static void Main(string[] args)
 2         {
 3             Tools.AppSettings["Test"] = "Love";                           //修改配置文件
 4             Console.WriteLine(ConfigurationManager.AppSettings["Test"]);  //传统方式 读取配置文件
 5             Console.WriteLine(Tools.AppSettings["Test"]);                 //工具类 读取配置文件
 6 
 7             Tools.ConnectionStrings["ConnString"] = "Data Source=127.0.0.1;Initial Catalog=master;User=sa;password=123.com;";
 8             Console.WriteLine(ConfigurationManager.ConnectionStrings["ConnString"]);
 9             Console.WriteLine(Tools.ConnectionStrings["ConnString"]);
10 
11             Tools.AppSettings["Test"] = "<Love>";
12             Console.WriteLine(ConfigurationManager.AppSettings["Test"]);
13             Console.WriteLine(Tools.AppSettings["Test"]);
14 
15             Console.ReadKey();
16         }

执行结果:

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

配置文件变化:

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

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

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

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

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
LinMeng LinMeng
3年前
vue中页面间跳转传值的两种方式(query,params)
两者都可以传递参数,区别是什么?query传参配置的是path,而params传参配置的是name,在params中配置path无效query在路由配置不需要设置参数,而params必须设置query传递的参数会显示在地址栏中params传参刷新会无效,但是query会保存传递过来的值,刷新不变;query:this.$route
Stella981 Stella981
2年前
Dubbo 扩展点加载机制:从 Java SPI 到 Dubbo SPI
!(https://oscimg.oschina.net/oscnet/up1aa4ada0efc8a144d35d25b3443d951c7e3.JPEG)SPI全称为ServiceProviderInterface,是一种服务发现机制。当程序运行调用接口时,会根据配置文件或默认规则信息加载对应的实现类。所以在程序中并没有直接指定使用接口
沸腾的木马 沸腾的木马
10个月前
关于dorker安装的wordpress无法上传主题和插件的解决办法
首先,容器里居然没有php.ini这个文件。第二,容器里的配置文件无法编辑解决办法在php官网下载php.ini这个文件,修改后通过主机copy到容器里1、要查找容器的名称或ID,可以使用以下命令:dockerps这将显示正在运行的容器的列表,类似于以下示
Stella981 Stella981
2年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Stella981 Stella981
2年前
Spring Cloud Alibaba Nacos Config 的使用
一、需求主要实现nacos作为配置中心的一些使用方法。二、实现功能1、加载productproviderdev.yaml配置文件2、实现配置的自动刷新3、实现加载多个配置文件
Stella981 Stella981
2年前
Android So动态加载 优雅实现与原理分析
背景:漫品Android客户端集成适配转换功能(基于目标识别(So库35M)和人脸识别库(5M)),导致apk体积50M左右,为优化客户端体验,决定实现So文件动态加载.!(https://oscimg.oschina.net/oscnet/00d1ff90e4b34869664fef59e3ec3fdd20b.png)点击上方“蓝字”关注我
Wesley13 Wesley13
2年前
35岁,真的是程序员的一道坎吗?
“程序员35岁是道坎”,“程序员35岁被裁”……这些话咱们可能都听腻了,但每当触及还是会感到丝丝焦虑,毕竟每个人都会到35岁。而国内互联网环境确实对35岁以上的程序员不太友好:薪资要得高,却不如年轻人加班猛;虽说经验丰富,但大部分公司并不需要太资深的程序员。但35岁危机并不是不可避免的,比如你可以不断精进技术,将来做技术管理或者
胖大海 胖大海
1年前
Centos7 yum安装mariadb
1配置yum源安装  yum配置文件/etc/yum.repos.dyuminstallmariadbmariadbserver2修改root用户默认密码updateusersetpasswordpassword("xxxx")whereuser"root"; 修改root用户密码flushprivileges;刷新权限,3.
贾蓁 贾蓁
3个月前
2023最新版-Web前端架构师(35周完结无密)
2023最新版Web前端架构师(35周完结无密)download》http://quangneng.com/3677/Web前端架构师是负责设计和构建高效、可扩展和可维护的前端Web应用程序的专家。他们通常具有深厚的技术背景,熟悉各种前端技术和工具,并能够