Click here to Skip to main content
12,397,805 members (59,409 online)
Rate this:
 
Please Sign up or sign in to vote.
See more: C# .NET .NET4 App.config
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>

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();
        }
    }
  }
}

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
Posted 13-Dec-12 23:26pm
Updated 14-Dec-12 1:13am
v2
Rate this: bad
 
good
Please Sign up or sign in to vote.

Solution 3

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.
  Permalink  
Comments
Monica Agrawal 15-Dec-12 5:25am
   
Thanks. This worked using Asynchronous call. I removed WaitForChanged. The console sometimes throws an error for accessing the file being used by another process. So I removed that from the FileChanged. Thanks again :)
Rate this: bad
 
good
Please Sign up or sign in to vote.

Solution 1

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
  Permalink  
Comments
Monica Agrawal 14-Dec-12 13:30pm
   
But I am not getting exceptions when I put the breakpoint in the catch blocks. Could this be a cross-threading issue ?

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

  Print Answers RSS
Top Experts
Last 24hrsThis month


Advertise | Privacy | Mobile
Web02 | 2.8.160721.1 | Last Updated 14 Dec 2012
Copyright © CodeProject, 1999-2016
All Rights Reserved. Terms of Service
Layout: fixed | fluid

CodeProject, 503-250 Ferrand Drive Toronto Ontario, M3C 3G8 Canada +1 416-849-8900 x 100