基本共识:

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, ); //将配置文件 设置为: 未分析 状态, 配置文件 将会在下次读取 时 重新分析.
//立即生效,而且效果 明显 —— 就喜欢这种 暴力做法。

一起反编译 ConfigurationManager 代码:

> 首先 下载 ILSpy 或 Reflector (本文使用的是 ILSpy.)

> 打开 ILSpy 搜索 ConfigurationManager,执行如下操作:

> 编写 反射代码,刷新 配置文件数据。(具体代码 在 文章最开始。)

额外提供 配置文件 修改的 工具类代码:

以下代码 实现如下功能:

> 执行 配置写入操作时,自动创建 .exe.config 文件,自动创建 appSettings  connectionString 节点。

> .exe.config 写入配置时,如果 相同的 key  name 存在,则修改,不存在 则创建。

> 额外的 审美操作

> 很多人习惯 appSettings 显示在 connectionString 前面。

> 很多人习惯 appSettings 在 最前面。

> appSettings 必须在 configSections 后面。(configSections 配置文件 扩展配置节点,只能写在第一个,否则 程序报错。)

 using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
using System.Xml; namespace InkFx.Utils
{
public partial class Tools
{ private static ConfigAppSetting m_AppSettings;
private static ConfigConnectionStrings m_ConnectionStrings; public static ConfigAppSetting AppSettings
{
get
{
if (m_AppSettings == null)
{
m_AppSettings = new ConfigAppSetting();
m_AppSettings.AppSettingChanged += OnAppSettingChanged;
}
return m_AppSettings;
}
}
public static ConfigConnectionStrings ConnectionStrings
{
get
{
if (m_ConnectionStrings == null)
{
m_ConnectionStrings = new ConfigConnectionStrings();
m_ConnectionStrings.ConnectionStringsChanged += OnConnectionStringsChanged;
}
return m_ConnectionStrings;
}
} private static void OnAppSettingChanged(string name, string value)
{
string configPath = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
if (!File.Exists(configPath))
{
const string content = @"<?xml version=""1.0""?><configuration></configuration>";
File.WriteAllText(configPath, content, Encoding.UTF8);
} XmlDocument doc = new XmlDocument();
doc.Load(configPath); XmlNode nodeConfiguration = doc.SelectSingleNode(@"configuration");
if (nodeConfiguration == null)
{
nodeConfiguration = doc.CreateNode(XmlNodeType.Element, "configuration", string.Empty);
doc.AppendChild(nodeConfiguration);
} XmlNode nodeAppSettings = nodeConfiguration.SelectSingleNode(@"appSettings");
if (nodeAppSettings == null)
{
nodeAppSettings = doc.CreateNode(XmlNodeType.Element, "appSettings", string.Empty);
if (!nodeConfiguration.HasChildNodes)
nodeConfiguration.AppendChild(nodeAppSettings);
else
{
//configSections 必须放在 第一个, 所以得 避开 configSections
XmlNode firstNode = nodeConfiguration.ChildNodes[];
bool firstNodeIsSections = string.Equals(firstNode.Name, "configSections", StringComparison.CurrentCultureIgnoreCase); if (firstNodeIsSections)
nodeConfiguration.InsertAfter(nodeAppSettings, firstNode);
else
nodeConfiguration.InsertBefore(nodeAppSettings, firstNode);
}
} string xmlName = FormatXmlStr(name);
XmlNode nodeAdd = nodeAppSettings.SelectSingleNode(@"add[@key='" + xmlName + "']");
if (nodeAdd == null)
{
nodeAdd = doc.CreateNode(XmlNodeType.Element, "add", string.Empty);
nodeAppSettings.AppendChild(nodeAdd);
} XmlElement nodeElem = (XmlElement)nodeAdd;
nodeElem.SetAttribute("key", name);
nodeElem.SetAttribute("value", value);
doc.Save(configPath); try { ConfigurationManager.RefreshSection("appSettings"); } catch (Exception) { }
}
private static void OnConnectionStringsChanged(string name, string value)
{
string configPath = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
if (!File.Exists(configPath))
{
const string content = @"<?xml version=""1.0""?><configuration></configuration>";
File.WriteAllText(configPath, content, Encoding.UTF8);
} XmlDocument doc = new XmlDocument();
doc.Load(configPath); XmlNode nodeConfiguration = doc.SelectSingleNode(@"configuration");
if (nodeConfiguration == null)
{
nodeConfiguration = doc.CreateNode(XmlNodeType.Element, "configuration", string.Empty);
doc.AppendChild(nodeConfiguration);
} XmlNode nodeAppSettings = nodeConfiguration.SelectSingleNode(@"appSettings");
XmlNode nodeConnectionStrings = nodeConfiguration.SelectSingleNode(@"connectionStrings");
if (nodeConnectionStrings == null)
{
nodeConnectionStrings = doc.CreateNode(XmlNodeType.Element, "connectionStrings", string.Empty);
if (!nodeConfiguration.HasChildNodes)
nodeConfiguration.AppendChild(nodeConnectionStrings);
else
{
//优先将 connectionStrings 放在 appSettings 后面
if (nodeAppSettings != null)
nodeConfiguration.InsertAfter(nodeConnectionStrings, nodeAppSettings);
else
{
//如果 没有 appSettings 节点, 则 configSections 必须放在 第一个, 所以得 避开 configSections
XmlNode firstNode = nodeConfiguration.ChildNodes[];
bool firstNodeIsSections = string.Equals(firstNode.Name, "configSections", StringComparison.CurrentCultureIgnoreCase); if (firstNodeIsSections)
nodeConfiguration.InsertAfter(nodeConnectionStrings, firstNode);
else
nodeConfiguration.InsertBefore(nodeConnectionStrings, firstNode);
}
}
} string xmlName = FormatXmlStr(name);
XmlNode nodeAdd = nodeConnectionStrings.SelectSingleNode(@"add[@name='" + xmlName + "']");
if (nodeAdd == null)
{
nodeAdd = doc.CreateNode(XmlNodeType.Element, "add", string.Empty);
nodeConnectionStrings.AppendChild(nodeAdd);
} XmlElement nodeElem = (XmlElement)nodeAdd;
nodeElem.SetAttribute("name", name);
nodeElem.SetAttribute("connectionString", value);
doc.Save(configPath); try
{
ConfigurationManager.RefreshSection("connectionString"); //RefreshSection 无法刷新 connectionString 节点
FieldInfo fieldInfo = typeof(ConfigurationManager).GetField("s_initState", BindingFlags.NonPublic | BindingFlags.Static);
if (fieldInfo != null) fieldInfo.SetValue(null, ); //将配置文件 设置为: 未分析 状态, 配置文件 将会在下次读取 时 重新分析.
}
catch (Exception) { }
} private static string FormatXmlStr(string value)
{
if (string.IsNullOrEmpty(value)) return string.Empty; string result = value
.Replace("<", "&lt;")
.Replace(">", "&gt;")
.Replace("&", "&amp;")
.Replace("'", "&apos;")
.Replace("\"", "&quot;");
return result;
//&lt; < 小于号
//&gt; > 大于号
//&amp; & 和
//&apos; ' 单引号
//&quot; " 双引号
} public class ConfigAppSetting
{
private readonly InnerIgnoreDict<string> m_Hash = new InnerIgnoreDict<string>(); public string this[string name]
{
get
{
string value = m_Hash[name];
if (string.IsNullOrWhiteSpace(value))
{
try { value = ConfigurationManager.AppSettings[name]; } catch(Exception) { }
m_Hash[name] = value;
return value;
}
return value;
}
set
{
m_Hash[name] = value;
try{ ConfigurationManager.AppSettings[name] = value; } catch(Exception) { }
if (AppSettingChanged != null) AppSettingChanged(name, value);
}
}
public AppSettingValueChanged AppSettingChanged; public delegate void AppSettingValueChanged(string name, string value);
}
public class ConfigConnectionStrings
{
private readonly InnerIgnoreDict<ConnectionStringSettings> m_Hash = new InnerIgnoreDict<ConnectionStringSettings>(); public string this[string name]
{
get
{
ConnectionStringSettings value = m_Hash[name];
if (value == null || string.IsNullOrWhiteSpace(value.ConnectionString))
{
try { value = ConfigurationManager.ConnectionStrings[name]; } catch (Exception) { }
m_Hash[name] = value;
return value == null ? string.Empty : value.ConnectionString;
}
return value.ConnectionString;
}
set
{ ConnectionStringSettings setting = new ConnectionStringSettings();
setting.Name = name;
setting.ConnectionString = value;
m_Hash[name] = setting;
//try { ConfigurationManager.ConnectionStrings[name] = setting; } catch (Exception) { }
if (ConnectionStringsChanged != null) ConnectionStringsChanged(name, value);
}
}
public ConnectionStringsValueChanged ConnectionStringsChanged; public delegate void ConnectionStringsValueChanged(string name, string value);
} private class InnerIgnoreDict<T> : Dictionary<string, T>
{
public InnerIgnoreDict(): base(StringComparer.CurrentCultureIgnoreCase)
{
} #if (!WindowsCE && !PocketPC)
public InnerIgnoreDict(SerializationInfo info, StreamingContext context) : base(info, context) { }
#endif private readonly object getSetLocker = new object();
private static readonly T defaultValue = default(T); public new T this[string key]
{
get
{
if (key == null) return defaultValue;
lock (getSetLocker) //为了 多线程的 高并发, 取值也 加上 线程锁
{
T record;
if (TryGetValue(key, out record)) return record;
else return defaultValue;
}
}
set
{
try
{
if (key != null)
{
lock (getSetLocker)
{
//if (!value.Equals(default(T)))
//{
if (base.ContainsKey(key)) base[key] = value;
else base.Add(key, value);
//}
//else
//{
// base.Remove(key);
//}
}
}
}
catch (Exception) { }
}
}
} }
}

工具类使用代码:

         static void Main(string[] args)
{
Tools.AppSettings["Test"] = "Love"; //修改配置文件
Console.WriteLine(ConfigurationManager.AppSettings["Test"]); //传统方式 读取配置文件
Console.WriteLine(Tools.AppSettings["Test"]); //工具类 读取配置文件 Tools.ConnectionStrings["ConnString"] = "Data Source=127.0.0.1;Initial Catalog=master;User=sa;password=123.com;";
Console.WriteLine(ConfigurationManager.ConnectionStrings["ConnString"]);
Console.WriteLine(Tools.ConnectionStrings["ConnString"]); Tools.AppSettings["Test"] = "<Love>";
Console.WriteLine(ConfigurationManager.AppSettings["Test"]);
Console.WriteLine(Tools.AppSettings["Test"]); Console.ReadKey();
}

执行结果:

配置文件变化:

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

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

『随笔』C# 程序 修改 ConfigurationManager 后,不重启 刷新配置的更多相关文章

  1. 解决Windows服务修改配置文件后必须重启的问题

      原文地址:http://www.cnblogs.com/jeffwongishandsome/archive/2011/04/24/2026381.html   解决方法:读取配置文件前先刷新文件 ...

  2. Node.js热部署代码,实现修改代码后自动重启服务方便实时调试

    写PHP等脚本语言的时候,已经习惯了修改完代码直接打开浏览器去查看最新的效果.而Node.js 只有在第一次引用时才会去解析脚本文件,以后都会直接访问内存,避免重复载入,这种设计虽然有利于提高性能,却 ...

  3. spring boot修改代码后无需重启设置,在开发时实现热部署

    Spring Boot在开发时实现热部署(开发时修改文件保存后自动重启应用)(spring-boot-devtools) 热部署是什么 大家都知道在项目开发过程中,常常会改动页面数据或者修改数据结构, ...

  4. The Data Way Vol.2 | 做个『单纯』的程序员还真不简单

    关于「The Data Way」 「The Data Way」是由 SphereEx 公司出品的一档播客节目.这里有开源.数据.技术的故事,同时我们关注开发者的工作日常,也讨论开发者的生活日常:我们聚 ...

  5. 『随笔』Socket 链接 必须 上下行 同时使用

    结论: > Socket 理论上 支持 只上行,或者 只下行. > 心跳包 必须是 上下行的 —— 心跳包请求(上行) - 心跳包响应(下行). > 如果 长时间 只有单向链接(只发 ...

  6. 『随笔』WCF开发那些需要注意的坑

    执行如下 批处理:"C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bin\svcutil.exe" http://127.0.0.1: ...

  7. 『随笔』.Net 底层 数组[] 的 基本设计探秘 512 子数组

    static void Main(string[] args) { Console.ReadKey(); //初始化数组 不会立即开辟内存字节, 只有实际给数组赋值时 才会开辟内存 // //猜测数组 ...

  8. Android程序意外Crash后自动重启

    1.自定义UncaughtExceptionHandler public class UnCeHandler implements UncaughtExceptionHandler { private ...

  9. Maven项目热部署,修改代码后不用重启tomcat服务器

    只需要在pom.xml文件中添加 <build> <finalName>MySSM</finalName> <!-- 指定部署的服务器类型 --> &l ...

随机推荐

  1. MVC如何在单独的类库中添加区域

    今天要做一个将区域放到单独的类库中的程序,其实就是多加几个引用的问题,但是我比较喜欢这种设计结构,因为这样的话可以把单独的应用逻辑放在单独的类库中处理,项目看起来更清晰分明,所以写了这个随笔. 首先创 ...

  2. redis状态查看

      https://redis.readthedocs.org/en/latest/server/slowlog.html   https://redis.readthedocs.org/en/lat ...

  3. CI 框架中 AR 操作

    Model 层中的部分代码 /** * CI 中的 AR 操作 * @author zhaoyingnan **/ public function mAR() { /*************** 查 ...

  4. C/C++ 位域

    //假设硬件平台是intel x86(little endian) typedef unsigned int uint32_t; void inet_ntoa(uint32_t in) { ]; re ...

  5. 一个fork的面试题

    前两天有人问了个关于Unix的fork()系统调用的面试题,这个题正好是我大约十年前找工作时某公司问我的一个题,我觉得比较有趣,写篇文章与大家分享一下.这个题是这样的: 题目:请问下面的程序一共输出多 ...

  6. linux alarm函数解除read write等函数的阻塞

    看到apue的第十章,说到alarm,pause可以实现sleep,可以让某些一直阻塞的函数超时,例如read,write.代码如下: static void sig_alrm(int signo) ...

  7. 【软件使用】用IntelliJ IDEA开发Android程序图解

    博主这里使用的最新的14.0.3版本,下载地址:http://www.jetbrains.com/idea/, 下载后直接安装就可以了. 环境: OS:Win 8 IDE:IntelliJ IDEA ...

  8. 边工作边刷题:70天一遍leetcode: day 86-2

    Best Meeting Point 要点: 题本身不难理解,manhattan distance.follow up就变成weighted了(因为一个地方可以有多个住户) 注意input是grid的 ...

  9. UESTC 876 爱管闲事 --DP

    题意:即求给定n个数字(a1,a2,……an),不改变序列,分成M份,使每一份和的乘积最大. 思路:dp[i][j]表示把前i个数字,分成j份所能得到的最大乘积. 转移方程:dp[i][j] = ma ...

  10. uGUI练习(二) Animate UI

    练习目标 通过Animation录制UI动画 一.步骤 1.创建一个Panel,下面再创建两个子Panel 2.修改Canvas的 Render Mode为Screen Space-Camer 3.为 ...