使用 FileSystemMonitoring 读取 app.config 中的更改并实时写入 app.config
Posted
技术标签:
【中文标题】使用 FileSystemMonitoring 读取 app.config 中的更改并实时写入 app.config【英文标题】:Using FileSystemMonitoring for reading changes in app.config and writing to app.config realtime 【发布时间】:2012-12-02 07:38:25 【问题描述】:我正在使用 FileSystemWatcher 来监视 app.config 文件中的任何更改。而且,写入配置文件。
这是我的代码:
MyApplication是主项目,DataCache是MyApplication中引用的类库。
using System;
using System.Threading;
using System.IO;
namespace MyApplication
public class Program
public static string rootFolderPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
public static string configFilePath = Path.Combine(rootFolderPath, "MyApplication.exe.config");static void Main(string[] args)
//First Time initializing the config file.
ConfigFileMonitor.SetConfigFileAtRuntime(configFilePath);
//Initializing the config file monitor on a separate thread.
Thread monitorThread = new Thread(ConfigFileMonitor.BeginConfigFilesMonitor);
monitorThread.Start();
WriteToConfigFile();
static void WriteToConfigFile()
Console.WriteLine(String.Format("Hello 0 1", DataCache.Section1Data.FirstNameString, DataCache.Section1Data.LastNameString));
string _firstName, _lastName = string.Empty;
do
Console.WriteLine("");
Console.Write("First Name : ");
_firstName = Console.ReadLine();
Console.Write("Last Name : ");
_lastName = Console.ReadLine();
if(_firstName.Length>0)
DataCache.Section1Data.FirstNameString = _firstName;
if(_lastName.Length>0)
DataCache.Section1Data.LastNameString = _lastName;
Console.WriteLine(String.Format("Hello 0 1", DataCache.Section1Data.FirstNameString, DataCache.Section1Data.LastNameString));
while (true);
using System;
using System.IO;
namespace MyApplication
/// <summary>
/// Monitors for any change in the app.config file
/// </summary>
public class ConfigFileMonitor
private static FileSystemWatcher _watcher;
private static string _configFilePath = String.Empty;
private static string _configFileName = String.Empty;
/// <summary>
/// Texts the files surveillance.
/// </summary>
public static void BeginConfigFilesMonitor()
try
string fileToMonitor = Program.configFilePath;
if (fileToMonitor.Length == 0)
Console.WriteLine("Incorrect config file specified to watch");
else
_configFileName = Path.GetFileName(fileToMonitor);
_configFilePath = fileToMonitor.Substring(0, fileToMonitor.IndexOf(_configFileName));
// Use FileWatcher to check and update only modified text files.
WatchConfigFiles(_configFilePath, _configFileName);
catch (Exception e1)
Console.WriteLine(e1.Message);
/// <summary>
/// Watches the files.
/// </summary>
/// <param name="targetDir">The target dir.</param>
/// <param name="filteredBy">The filtered by.</param>
static void WatchConfigFiles(string targetDir, string filteredBy)
try
_watcher = new FileSystemWatcher();
_watcher.Path = targetDir;
_watcher.Filter = filteredBy;
_watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite;
_watcher.EnableRaisingEvents = true;
_watcher.Changed += new FileSystemEventHandler(FileChanged);
_watcher.WaitForChanged(WatcherChangeTypes.Changed);
catch (Exception e1)
Console.WriteLine(e1.Message);
/// <summary>
/// Handles the Changed event of the File control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.IO.FileSystemEventArgs"/> instance containing the event data.</param>
protected static void FileChanged(object sender, FileSystemEventArgs e)
try
_watcher.EnableRaisingEvents = false;
string filechange = e.FullPath;
Console.WriteLine("Configuration File: " + filechange + "changed");
//Since the file is changed - We have reload the configuration settings again.
SetConfigFileAtRuntime(Path.Combine(Program.rootFolderPath, "MyApplication.exe.config"));
Console.WriteLine(String.Format("New Name : 0 1", DataCache.Section1Data.FirstNameString, DataCache.Section1Data.LastNameString));
_watcher.EnableRaisingEvents = true;
catch (Exception e1)
Console.WriteLine(e1.Message);
/// <summary>
/// Sets the config file at runtime.
/// </summary>
/// <param name="configFilePath"></param>
public static void SetConfigFileAtRuntime(string configFilePath)
string runtimeconfigfile;
try
if (configFilePath.Length == 0)
Console.WriteLine("Please specify a config file to read from ");
else
runtimeconfigfile = configFilePath;
_configFileName = Path.GetFileName(configFilePath);
_configFilePath = configFilePath.Substring(0, configFilePath.IndexOf(_configFileName));
// Specify config settings at runtime.
//System.Configuration.Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
DataCache.DataConfigSection.config = System.Configuration.ConfigurationManager.OpenExeConfiguration(configFilePath);
//Similarly you can apply for other sections like SMTP/System.Net/System.Web etc..
//But you have to set the File Path for each of these
//config.AppSettings.File = runtimeconfigfile;
//This doesn't actually going to overwrite you Exe App.Config file.
//Just refreshing the content in the memory.
DataCache.DataConfigSection.config.Save(System.Configuration.ConfigurationSaveMode.Modified);
//Refreshing Config Section
//ConfigurationManager.RefreshSection("appSettings");
System.Configuration.ConfigurationManager.RefreshSection("MySectionGroup/Section1");
DataCache.Section1Data.configSection1 = null;
catch (Exception e1)
Console.WriteLine(e1.Message);
<?xml version="1.0"?>
<configuration>
<configSections>
<sectionGroup name="MySectionGroup">
<section name="Section1" type="DataCache.DataConfigSection, DataCache" allowLocation="false" allowDefinition="Everywhere"/>
<section name="Section2" type="DataCache.DataConfigSection, DataCache" allowLocation="false" allowDefinition="Everywhere"/>
</sectionGroup>
</configSections>
<MySectionGroup>
<Section1>
<name
firstName ="Pierce"
lastName ="Brosnan"
/>
</Section1>
</MySectionGroup>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
</startup>
</configuration>
这里是 DataCache 类:
using System;
namespace DataCache
public sealed class DataConfigSection : System.Configuration.ConfigurationSection
public static System.Configuration.Configuration config = System.Configuration.ConfigurationManager.OpenExeConfiguration(
System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetAssembly(typeof(DataConfigSection)).Location), "MyApplication.exe"));
// Create a "name" element.
[System.Configuration.ConfigurationProperty("name")]
public NameElement Name
get
return (NameElement)this["name"];
set
this["name"] = value;
// Define the "name" element
// with firstName, secondName attributes.
public class NameElement : System.Configuration.ConfigurationElement
[System.Configuration.ConfigurationProperty("firstName", DefaultValue = "abcd", IsRequired = true)]
public String FirstName
get
return (String)this["firstName"];
set
this["firstName"] = value;
[System.Configuration.ConfigurationProperty("lastName", DefaultValue = "xyz", IsRequired = true)]
public String LastName
get
return (String)this["lastName"];
set
this["lastName"] = value;
namespace DataCache
public class Section1Data
public static volatile DataConfigSection configSection1;
private static object syncRoot = new System.Object();
public const string Section1ConfigSectionName = "MySectionGroup/Section1";
private Section1Data()
public static DataConfigSection ConfigSection1
get
lock (syncRoot)
if (configSection1 == null)
DataConfigSection.config = System.Configuration.ConfigurationManager.OpenExeConfiguration(System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetAssembly(typeof(DataConfigSection)).Location), "MyApplication.exe"));
configSection1 = (DataConfigSection)DataConfigSection.config.GetSection(Section1ConfigSectionName);
return configSection1;
public static string FirstNameString
get
return ConfigSection1.Name.FirstName.ToString();
set
ConfigSection1.Name.FirstName = value;
DataConfigSection.config.Save();
public static string LastNameString
get
return ConfigSection1.Name.LastName.ToString();
set
ConfigSection1.Name.LastName = value;
DataConfigSection.config.Save();
问题是,如果从控制台输入对名称进行了任何更改,则 FileChanged 会被调用一次,并且后续更改不会触发它。此外,如果手动对配置文件进行更改,它可以正常工作,直到从控制台进行更改为止,事件会一直触发。
我不确定为什么这会表现得很奇怪。有人能帮我一下吗。
谢谢, 莫妮卡
【问题讨论】:
_watcher.EnableRaisingEvents = false 行(在 FileChanged 方法的末尾)是否正确?我希望它将 EnableRaisingEvents 设置为 true。 是的,你是对的。那是一个错字。在上面的帖子中进行了相同的编辑。但是问题还是一样的:( 【参考方案1】:您的代码非常复杂。我的 watcher 代码大约 10 行。
没有必要将 watcher 的初始化卸载到另一个线程。在主线程上初始化它。 watcher 在后台线程中监视文件并在发生更改时触发事件,因此无需调用 WaitForChanged() 方法。
我检查了我的工作代码并将其与您的进行了比较,唯一的两个区别是:
1. 我的过滤器是:NotifyFilters.LastWrite | NotifyFilters.文件名 | NotifyFilters.DirectoryName
2. 我不调用 WaitForChanged() 方法
3. 我已将[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
应用于与观察者交互的所有方法。
此外,请确保您正在监视的文件不在系统目录之一中...监视程序无法很好地处理这些文件。因为想在 C: 上看文件,所以我把头撞了半天。
【讨论】:
【参考方案2】:短版是:
this.Watcher = new FileSystemWatcher();
this.Watcher.Path = this.Dir;
this.Watcher.Filter = this.File;
this.Watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
this.Watcher.EnableRaisingEvents = true;
this.Watcher.Changed += this.OnFileChange;
但是,这里有一个更复杂(和丑陋)的示例,它是从需要一些额外处理(除了读取文件之外)的实时源中取出的。
public partial class FileModule
private ConcurrentDictionary<string, InputFileInfo> inputFileList = new ConcurrentDictionary<string, InputFileInfo>();
public FileModule()
this.InitializeInputFileWatchers();
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
public void EnableOrDisableRaisingEventsForFileWatchers(bool enable)
foreach (var el in this.inputFileList)
el.Value.Watcher.EnableRaisingEvents = enable;
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
private void InitializeInputFileWatchers()
for (int i = 0; i < this.InputFiles.Count; i++)
if (File.Exists(this.InputFiles[i]))
InputFileInfo info = new InputFileInfo();
info.Fullpath = ((FileModuleSettings)this.Settings).InputFiles[i];
info.Watcher.Changed += this.OnFileChange;
this.inputFileList.AddOrUpdate(info.Fullpath, info, (e, v) => return info; );
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
private void OnFileChange(object source, FileSystemEventArgs e)
InputFileInfo info;
if (this.inputFileList.TryGetValue(e.FullPath, out info))
DateTime lastWriteTime = System.IO.File.GetLastWriteTime(e.FullPath);
if (info.LastHandledChange != lastWriteTime)
TimeSpan span = lastWriteTime.Subtract(info.LastHandledChange);
if (span.Days == 0 && span.Hours == 0 && span.Minutes == 0 && span.Seconds == 0 && span.TotalMilliseconds < this.MinimumFileChangePeriod)
// Event ignored due to required minimum file change period;
else
info.LastHandledChange = lastWriteTime;
this.inputFileList.AddOrUpdate(e.FullPath, info, (a, v) => return info; );
lock (this.readLockerObject)
this.ReadFile(e.FullPath);
private bool ReadFile(string filepath, int count, bool directReading)
StreamReader sr = this.OpenStreamReader(file);
if (sr != null)
string line;
string[] split;
int signalId;
double value;
while ((line = sr.ReadLine()) != null)
// do sth. with line
internal class InputFileInfo : IDisposable
public string Dir get; private set;
public string File get; private set;
public FileSystemWatcher Watcher get; private set;
public DateTime LastHandledChange get; set;
private string fullpath;
public string Fullpath
get
return this.fullpath;
set
this.fullpath = BaseHelper.GetFullFilePath(value);
this.Dir = Directory.GetParent(this.fullpath).ToString();
this.File = this.fullpath.Replace(this.Dir + "\\", string.Empty);
this.Watcher = new FileSystemWatcher();
this.Watcher.Path = this.Dir;
this.Watcher.Filter = this.File;
this.Watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
this.Watcher.EnableRaisingEvents = true;
public void Dispose()
if (this.Watcher != null)
this.Watcher.Dispose();
this.Watcher = null;
【讨论】:
以上是关于使用 FileSystemMonitoring 读取 app.config 中的更改并实时写入 app.config的主要内容,如果未能解决你的问题,请参考以下文章