『隨筆』C# 程式 修改 ConfigurationManager 後,不重啟 刷新配置

来源:http://www.cnblogs.com/shuxiaolong/archive/2016/09/07/20160907_1432.html
-Advertisement-
Play Games

基本共識: ConfigurationManager 自帶緩存,且不支持 寫入。 如果 通過 文本寫入方式 修改 配置文件,程式 無法刷新載入 最新配置。 PS. Web.config 除外:Web.config 修改後,網站會重啟 (即 Web 程式 也無法在 運行時 刷新配置)。 為什麼要在程式 ...


基本共識:

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         }

 

 

執行結果:

 

配置文件變化:

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

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

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 雖然有很多話想多,但是提筆才發現自己的語言組織能力有多差,差到不知道從何說起。 今日如往常一樣的修改bug,遇到解決不了的問題直接複製到百度里。通過網路站在前人的肩膀上真的是一件非常爽的事情,基本上第一頁看不完就能把問題解決完,更爽的是,你既可以獲得清晰的步驟去解決想解決眼下的問題,又可以深究其原理 ...
  • .NetCore中的日誌(1)日誌組件解析 0x00 問題的產生 日誌記錄功能在開發中很常用,可以記錄程式運行的細節,也可以記錄用戶的行為。在之前開發時我一般都是用自己寫的小工具來記錄日誌,輸出目標包含控制台、文本文件、資料庫,一般都是創建全局的Logger,在需要記錄日誌的地方調用相應的Logge ...
  • 一.類自動屬性 1 public class Person 2 { 3 //自動屬性 4 public string Name { get; set; } 5 6 private int _age; 7 8 public int age { 9 get { return _age; } 10 set ...
  • 接上一篇:ABP教程(三)- 開始一個簡單的任務管理系統 – 後端編碼 1.實現UI端的增刪改查 1.1添加增刪改查代碼 打開SimpleTaskSystem.sln解決方案,添加一個“包含視圖的MVC 5控制器(使用EntityFramework)”TaskController控制器,添加成功後我 ...
  • 流:(I/O)輸入輸出流 分類:文件流,記憶體流,網路流 流的操作一般要放在try catch裡面,操作文件網路容易出現異常 命名空間:using system .IO;using system .Text; //二進位轉換需要的命名空間 一:類:FileStream:文件流 1.構造:一般用第三個重 ...
  • 文件及文件夾操作: C/S:WinForm可以操作客戶端文件 Client ServerB/S:Brower Server 命名空間:using system .IO; 1. File類: 創建:File.Create(路徑);創建文件,返回FileStream FileStream fs = Fi ...
  • 1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Drawing; 5 using System.Data; 6 using System.Linq; 7 ...
  • 進程:進程是一個具有獨立功能的程式關於某個數據集合的一次運行活動。它可以申請和擁有系統資源,是一個動態的概念,是一個活動的實體。Process 類,用來操作進程。 命名空間:using System.Diagnostics; Process.Start("calc"); //計算器Process.S ...
一周排行
    -Advertisement-
    Play Games
  • 一:背景 1. 講故事 前些天有位朋友找到我,說他們的程式會偶發性的卡死一段時間,然後又好了,讓我幫忙看下怎麼回事?窗體類的程式解決起來相對來說比較簡單,讓朋友用procdump自動抓一個卡死時的dump,拿到dump之後,上 windbg 說話。 二:WinDbg 分析 1. 主線程在做什麼 要想 ...
  • 功能說明 使用ListView時,希望可以在單元格顯示圖片或其他控制項,發現原生的ListView不支持,於是通過拓展,實現ListView可以顯示任意控制項的功能,效果如下: 實現方法 本來想著在單元格裡面實現控制項的自繪的,但是沒找到辦法,最後是通過在單元格的錶面顯示對應控制項的,浮於錶面達到目的。 實 ...
  • 由於.NET Framework 4.0 是比較古老的版本,只有New Relic 7.0以下的版本才會支持.NET Framework 4.0的引用程式。 Technical support for .NET Framework 4.0 or lower 你可以參考這個官方Install New ...
  • 前言 隨著 DEV24.1.3 的發佈,XAF Blazor 中的屬性編輯器(PropertyEditor)也進行了很大的改動,在使用體驗上也更接近 WinForm 了,由於進行了大量的封裝,理解上沒有 WinForm 直觀,所以本文通過對屬性編輯器的原理進行解析,並對比新舊版本中的變化,使大家能夠 ...
  • OPC基金會提供了OPC UA .NET標準庫以及示常式序,但官方文檔過於簡單,光看官方文檔和示常式序很難弄懂OPC UA .NET標準庫怎麼用,花了不少時間摸索才略微弄懂如何使用,以下記錄如何從一個控制台程式開發一個OPC UA伺服器。 安裝Nuget包 安裝OPCFoundation.NetSt ...
  • 今天在技術群里,石頭哥向大家提了個問題:"如何在一個以System身份運行的.NET程式(Windows Services)中,以其它活動的用戶身份啟動可互動式進程(桌面應用程式、控制台程式、等帶有UI和互動式體驗的程式)"? 我以前有過類似的需求,是在GitLab流水線中運行帶有UI的自動化測試程 ...
  • .Net 中提供了一系列的管理對象集合的類型,數組、可變列表、字典等。從類型安全上集合分為兩類,泛型集合 和 非泛型集合,傳統的非泛型集合存儲為Object,需要類型轉。而泛型集合提供了更好的性能、編譯時類型安全,推薦使用。 ...
  • 在以前我做程式的時候,一般在登錄視窗裡面顯示程式名稱,登錄視窗一般設置一張背景圖片,由於程式的名稱一般都是確定的,所以也不存在太大的問題,不過如果客戶定製不同的系統的時候,需要使用Photoshop修改下圖層的文字,再生成圖片,然後替換一下也可以了。不過本著減少客戶使用繁瑣性,也可以使用空白名稱的通... ...
  • 一:背景 1. 講故事 在dump分析的過程中經常會看到很多線程卡在Monitor.Wait方法上,曾經也有不少人問我為什麼用 !syncblk 看不到 Monitor.Wait 上的鎖信息,剛好昨天有時間我就來研究一下。 二:Monitor.Wait 底層怎麼玩的 1. 案例演示 為了方便講述,先 ...
  • 目錄前言學習參考過程總結: 前言 做個自由仔。 學習參考 ChatGpt; https://www.cnblogs.com/zhili/p/DesignPatternSummery.html(大佬的,看了好多次) 過程 原由: 一開始只是想查查鏈式調用原理,以為是要繼承什麼介面,實現什麼方法才可以實 ...