你的位置:首页 > ASP.net教程

[ASP.net教程]『随笔』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. 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 = @"<?"; 51         File.WriteAllText(configPath, content, Encoding.UTF8); 52       } 53  54       new  55       doc.Load(configPath); 56  57       @"configuration"); 58       if (nodeConfiguration == null) 59       { 60         nodeConfiguration = doc.CreateNode("configuration", string.Empty); 61         doc.AppendChild(nodeConfiguration); 62       } 63  64       @"appSettings"); 65       if (nodeAppSettings == null) 66       { 67         nodeAppSettings = doc.CreateNode("appSettings", string.Empty); 68         if (!nodeConfiguration.HasChildNodes) 69           nodeConfiguration.AppendChild(nodeAppSettings); 70         else 71         { 72           //configSections 必须放在 第一个, 所以得 避开 configSections 73           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  Format 84       @"add[@key='" + "']"); 85       if (nodeAdd == null) 86       { 87         nodeAdd = doc.CreateNode("add", string.Empty); 88         nodeAppSettings.AppendChild(nodeAdd); 89       } 90  91        ( 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 = @"<?";104         File.WriteAllText(configPath, content, Encoding.UTF8);105       }106 107       new 108       doc.Load(configPath);109 110       @"configuration");111       if (nodeConfiguration == null)112       {113         nodeConfiguration = doc.CreateNode("configuration", string.Empty);114         doc.AppendChild(nodeConfiguration);115       }116 117       @"appSettings");118       @"connectionStrings");119       if (nodeConnectionStrings == null)120       {121         nodeConnectionStrings = doc.CreateNode("connectionStrings", string.Empty);122         if (!nodeConfiguration.HasChildNodes)123           nodeConfiguration.AppendChild(nodeConnectionStrings);124         else125         {126           //优先将 connectionStrings 放在 appSettings 后面127           if (nodeAppSettings != null)128             nodeConfiguration.InsertAfter(nodeConnectionStrings, nodeAppSettings);129           else130           {131             //如果 没有 appSettings 节点, 则 configSections 必须放在 第一个, 所以得 避开 configSections132             0];133             bool firstNodeIsSections = string.Equals(firstNode.Name, "configSections", StringComparison.CurrentCultureIgnoreCase);134 135             if (firstNodeIsSections)136               nodeConfiguration.InsertAfter(nodeConnectionStrings, firstNode);137             else138               nodeConfiguration.InsertBefore(nodeConnectionStrings, firstNode);139           }140         }141       }142 143       string  Format144       @"add[@name='" + "']");145       if (nodeAdd == null)146       {147         nodeAdd = doc.CreateNode("add", string.Empty);148         nodeConnectionStrings.AppendChild(nodeAdd);149       }150 151        (152       nodeElem.SetAttribute("name", name);153       nodeElem.SetAttribute("connectionString", value);154       doc.Save(configPath);155 156       try157       {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 Formatstring value)166     {167       if (string.IsNullOrEmpty(value)) return string.Empty;168 169       string result = value170         .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 ConfigAppSetting185     {186       private readonly InnerIgnoreDict<string> m_Hash = new InnerIgnoreDict<string>();187 188       public string this[string name]189       {190         get191         {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         set202         {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 ConfigConnectionStrings213     {214       private readonly InnerIgnoreDict<ConnectionStringSettings> m_Hash = new InnerIgnoreDict<ConnectionStringSettings>();215 216       public string this[string name]217       {218         get219         {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         set230         {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 #endif256 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         get263         {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         set273         {274           try275           {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                 //else286                 //{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     }

 

 

执行结果:

 

配置文件变化:

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

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