• 『随笔』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         }

    执行结果:

    配置文件变化:

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

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

  • 相关阅读:
    云主机上搭建squid3代理服务器
    常见问题集锦
    [DFNews] Guidance推出EnCase v7.06以及EnCase Imager 7.06
    [计算机取证] JumpLists file names and AppID calculator
    [eDiscovery] The Longterm Preservation of Digital Evidence
    [DFNews] GSI发布EnCase v7.07
    [DFNews] eDEC发布“狼蛛”2.0手机取证系统
    [手机取证] CelleBrite Android Lock Bypass
    [DFNews] CelleBrite发布可视化关联分析软件Link Analysis 1.7
    [DFNews] EnCE认证变化,v6认证及相关课程即将取消
  • 原文地址:https://www.cnblogs.com/shuxiaolong/p/20160907_1432.html
Copyright © 2020-2023  润新知