基本共识:
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("<", "<")171 .Replace(">", ">")172 .Replace("&", "&")173 .Replace("'", "'")174 .Replace("\"", """);175 return result;176 //< < 小于号 177 //> > 大于号 178 //& & 和 179 //' ' 单引号 180 //" " 双引号 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 }
执行结果:
配置文件变化:
> 程序执行前,删除配置文件。
> 程序执行后,自动生成配置文件。
原标题:『随笔』C# 程序 修改 ConfigurationManager 后,不重启 刷新配置
关键词:C#