1. 程式人生 > >C# 程序修改config文件後,不重啟程序刷新配置ConfigurationManager

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

ima als .exe names nap first 存在 為什麽 default

基本共識:

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         }
技術分享圖片

執行結果:

技術分享圖片

配置文件變化:

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

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

技術分享圖片

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

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