使用FileSystemMonitoring读取app.config中的更改并写入app.config实时 [英] Using FileSystemMonitoring for reading changes in app.config and writing to app.config realtime

查看:70
本文介绍了使用FileSystemMonitoring读取app.config中的更改并写入app.config实时的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用FileSystemWatcher来监控app.config文件中的任何更改。并且,写入配置文件。



这是我的代码:



MyApplication是主要项目,DataCache是​​一个clas库,在MyApplication。





I am using FileSystemWatcher to monitor any changes in the app.config file. And also, writing to the config file.

Here is my code :

MyApplication is the main project and DataCache is a clas library referenced in 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类:





Here are the DataCache classes :

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被调用一次,后续更改不会触发它。此外,如果手动更改配置文件,它可以正常工作,直到事件继续发生,直到从控制台进行更改。



我不知道为什么是这表现得很奇怪。任何人都可以帮助我。



谢谢,Monica



The issue is if there is any change made to the name from console input, FileChanged gets called once and subsequent changes do not fire it. Moreover, if changes are made manually to the config file, it works fine till and events keep firing till a change is made from console.

I am not sure why is this behaving weird. Can anyone help me here.

Thanks, Monica

推荐答案

<$ c $中的代码c> FileSystemWatcher 线程不太正确,因为您正在使用这两个事件和 WaitForChanged 。它应该是一个或另一个。



FileSystemWatcher 可以在两种模式下使用

1)异步,即设置 EnableRaisingEvents == true并将处理程序附加到事件。



2)同步,即设置 EnableRaisingEvents == false并调用 WaitForChanged 方法来阻止线程直到发生某些事情。 WaitforChangedResult 结构包含有关导致返回方法的信息。



在你的情况下,我不是'' t认为你需要线程, FileSystemWatcher 可以在异步模式下运行。它只是在包含 Console.ReadLine()的数据输入循环启动之前初始化。 FileSystemWatcher事件在 ThreadPool 线程上通知,因此消息仍将写入控制台。



允许多个线程写入控制台(设计中固有的)的问题是来自 FileSystemWatcher的消息可以随时出现,并在您的用户输入提示中混入。而不是尝试修复它,我建议使用 System.Windows.Forms 更容易编写一些应用程序,其中可以使用输入和输出的专用控件。



Alan。
The code in the FileSystemWatcher thread is not quite right as you are using both events and WaitForChanged. It really should be one or the other.

The FileSystemWatcher can be used in two modes
1) Asynchronously, i.e. set EnableRaisingEvents == true and attach handlers to the events.

2) Synchronously, i.e. set EnableRaisingEvents == false and call the WaitForChanged method to block the thread until something happens. The WaitforChangedResult structure contains the information about whatever caused the method to return.

In your case I don''t think that you need the thread at all and the FileSystemWatcher can be operated in asynchronous mode. It would simply be initialised before the data entry loop containing Console.ReadLine() is started. The FileSystemWatcher events are notified on ThreadPool threads and so the messages would still be written to the console.

The problem with allowing more than one thread to write to the console, which is inherent in your design, is that the messages from the FileSystemWatcher can occur at any time and become mixed in with your prompts for user input. Rather than try to fix that I would suggest that some applications are more easily written with System.Windows.Forms where dedicated controls for input and output may be used.

Alan.


代码,我的猜测是;



_watcher .EnableRaisingEvents = true;



没有被解雇的方法



protected static void FileChanged (对象发送者,FileSystemEventArgs e)



这可能是由于以下某条线引起的异常而发生的:



============================================= ===

from the code, my guess is ;

_watcher.EnableRaisingEvents = true;

is not getting fired which is in the method

protected static void FileChanged(object sender, FileSystemEventArgs e)

this could be happening due to a exception caused at one of the below lines:

================================================
SetConfigFileAtRuntime(Path.Combine(Program.rootFolderPath, "MyApplication.exe.config"));

            Console.WriteLine(String.Format("New Name : {0} {1}", DataCache.Section1Data.FirstNameString, DataCache.Section1Data.LastNameString));





=========================================== =====



希望这会有所帮助。

免责声明:只是我的猜测,基于可视化代码审查



================================================

hope this helps.
disclaimer : just my guess , based on a visual code review


这篇关于使用FileSystemMonitoring读取app.config中的更改并写入app.config实时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆