随笔- 81  评论- 713  文章- 1 

『随笔』C# 程序 修改 ConfigurationManager 后,不重启 刷新配置

基本共识:

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

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

 

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

以下代码 实现如下功能:

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

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

> 额外的 审美操作

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

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

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

 

 

  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         }

 

 

执行结果:

 

配置文件变化:

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

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

 

posted on 2016-09-07 14:42  InkFx  阅读(...)  评论(...编辑  收藏