FileSystemWatcher to Monitor when External configuration file is changed (C#)

It is quite interesting thoughts and ideas i have, since I wrote my last post about using External Configuration files in .NET Applications.

This is a followup to my previous article. So I would n’t be discussing it again. If you need full understanding about using External config file in Windows/Console/Web applications please read my previous article.

The problem when we use external configuration file is that we wouldn’t get updated result/latest values from the config file whenever the external configuration file is modified. It’s because we are linking to an external file and applications can detect changes to the config file as long as the file is within the application base folder. So ideally ASP.NET or Windows Application or Console Application cannot automatically detect an external config it refering is changed. This we have to do it through custom implementation

I got an idea, which would be like I will write a FileSystemWatcher implementation to watch/monitor  for changes in external configuration file and reload the configuration settings the moment it found that file has changed.

This would ideally be cool, we don’t have to worry whether we get latest information or not. FileSystemWatcher will ideally capture if any changes occurred and our few lines of implementation code will refresh the configuration data in the application and we get the latest information from config.

To know more details about System.IO.FileSystemWatcher class, i would suggest reading the MSDN article here

I could give a brief introduction. System.IO.FileSystemManager class has the functionality to watch a file or a folder in the system for changes made to it or any other alteration to it.
File System Watcher will monitor the file or folder. When a new file is created or existing file is modified or deleted or renamed, it can capture that change and raise/notify through events.

There are generally 4 events we can talk about here
Created
Changed
Deleted
Renamed

I hope the naming of the events itself makes sense what it does.

My sample implementation utilizes the “Changed” event because my requirement is to get notified when config file content is modified.

Here is the code I made

External.config

<?xml version="1.0"?>

<appSettings>

  <add key="Web_App_Name" value="Dream Works India Tech-003-990"/>
  <add key="Web_App_ID" value="E1635BA6-0714-4496-A852-18868A0EB591"/>
</appSettings>







The main program.

Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;

namespace DreamConsoleApp
{
    class Program
    {
        /// <summary>
        /// Mains the specified args.
        /// </summary>
        /// <param name="args">The args.</param>
        static void Main(string[] args)
        {

            string configFilePath = @"C:DemoExternal.config";

            //First Time initializing to the config file. 
            ConfigFileMonitor.SetConfigFileAtRuntime(configFilePath);  

            string webAppName = ConfigurationManager.AppSettings["Web_App_Name"];
            string webAppId = ConfigurationManager.AppSettings["Web_App_ID"];

            //Initializing the config file monitor.
            ConfigFileMonitor.BeginConfigFilesMonitor(configFilePath);

            
            // Wait for the user to quit the program.
            Console.WriteLine("Press 'q' to quit the Application.");
            while 
                (
                Console.Read() != 'q'
                ) ;

            //Some way the application should continue running, 
            //other wise the file system watcher would not continue watching. 
            //To solve this the best way would be implementing in to a Windows service.
            
        }

    }
}


The File Watcher watches my external config file for changes.

Do not worry this is just a sample code, just a simple solution. You can implement it in your own way, as per your need.

One thing the application should continue running, other wise the file system watcher would not continue watching.

To solve this the best way would be implementing in to a Windows service.

ConfigFileMonitor.cs


using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;

namespace DreamConsoleApp
{
    public class ConfigFileMonitor
    {
        private static FileSystemWatcher _watcher;
        private static string _configFilePath = "";
        private static string _configFileName = "";

        /// <summary>
        /// Texts the files surveillance.
        /// </summary>
       public static void BeginConfigFilesMonitor(string fileToMonitor)
        {

            if (fileToMonitor.Length == 0)
            {
                Console.WriteLine("Please specify a config file to watch");
                Console.Write("> "); // prompt
                //runtimeconfigfile = Console.ReadLine();
            }
            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);

        }

        /// <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)
        {
            _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, 24 * 60 * 60 * 1000);  //Wait for 24 hours ( 24 Hours * 60 Mins * 60 Sec * 1000 Milli second)

            
        }


        /// <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)
        {
            string filechange = e.FullPath;

            Console.WriteLine("Configuration File: " + filechange + "changed");
           
           //Since the file is changed - We have reload the configuration settings again.
           SetConfigFileAtRuntime(@"C:DemoExternal.config");

            string webAppName = ConfigurationManager.AppSettings["Web_App_Name"];
            string webAppId = ConfigurationManager.AppSettings["Web_App_ID"];


            Console.WriteLine("New Web App Name :: " + webAppName);
            Console.WriteLine("New Web App ID   :: " + webAppId);

        }


        /// <summary>
        /// Sets the config file at runtime.
        /// </summary>
        /// <param name="configFilePath"></param>
        public static void SetConfigFileAtRuntime(string configFilePath)
        {
            string runtimeconfigfile;

            if (configFilePath.Length == 0)
            {
                Console.WriteLine("Please specify a config file to read from ");
                Console.Write("> "); // prompt
                runtimeconfigfile = Console.ReadLine();
            }
            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);

            //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.
            config.Save(ConfigurationSaveMode.Modified);

            //Refreshing Config Section
            ConfigurationManager.RefreshSection("appSettings");
        }

       
    }



}


Here is the result


I hope this helps to give a self explanatory solution which you can interpret from the code and comments on it.

This is just a Quick Thought.. I will test and update the code with much more perfection on later time.

That’s all for now. If any help needed, please leave a comment