Geeks With Blogs
Alex's Blog-o-monium

Being able to change file-based configuration settings in runtime is a nice facet in the ASP.NET world. Low-friction and seamless, this little feature allows for traffic to go on uninterrupted and helps avoid flushing AppDomain memory just to reload config. settings.

It's a different story when dealing with Console or Service applications, though. One has to restart the application/service to load up new config. settings.

To avoid losing data or interrupting a running process: one work-around is to hack out event handling routines prior to shutdown/restart and deal with graceful housekeeping there.

This is easy with Service (‘OnStop’ event: http://tinyurl.com/8vok4vj), little more difficult for Console (‘SetConsoleCtrlHandler’ interop: http://tinyurl.com/8bvnqzk). However, even with these tricks you must still restart the executable.

The solution which works for me (and I have to deal with large footprint, high-volume, crazy threaded Services/Console apps) is to simply force a periodic refresh against the config. file in runtime. Here's how I put it together:


1. Let's say, there's a class responsible for dealing with configuration upkeep for an application: SomeApplication, this upkeep class is called: SomeApplicationConfiguration.

public class SomeApplicationConfiguration : BaseConfiguration

(note BaseConfiguration, more on this later)
   
   
2. There’re simple static properties in such class, each corresponding to a configuration value for my SomeApplication ..application:

  1. public static int SomeNumericConfigurationValue
  2. {
  3.     get
  4.     {
  5.         return GetValueFromConfiguration<int>("SomeNumericConfigurationValue") != (default(int)) ?
  6.             GetValueFromConfiguration<int>("SomeNumericConfigurationValue") : 10000;
  7.     }
  8. }

(the default implementation would be used if, for some reason, this value is missing from the config. file so we replace it with some hard-coded default, a value of 10000 here)
     
       
3. Next, is the GetValueFromConfiguration method (we're almost at the 'meat' of the solution, bear with me).

This is a static method and it's part of the BaseConfiguration class mentioned in the first section. It uses some TypeConverter magic (http://tinyurl.com/cjvf6j3) to help with type conversion:

  1. public static T GetValueFromConfiguration<T>(String key)
  2. {
  3.     TypeConverter typeConverter = TypeDescriptor.GetConverter(typeof(T));
  4.  
  5.     if (AppSettings.AllKeys.ToList().Contains(key))
  6.         return (T)typeConverter.ConvertFromInvariantString(AppSettings[key]);
  7.     else
  8.         return default(T);
  9. }
               

4. Ok, so, the AppSettings property is ‘where it’s at’. This is the property which forces a periodic refresh of the configuration values against the .config file.

First, we keep a private static DateTime _refreshTime = DateTime.Now; field to use as a "bookmark" keeping track of the last time BaseConfiguration class pulled values directly from the file.

Secondly, we'll need a way to prevent threading collisions: I usually prefer the light-weight ReaderWriterLockSlim (http://tinyurl.com/btwngm4) type:

private static ReaderWriterLockSlim _syncRoot = new ReaderWriterLockSlim();

Thirdly, and lastly, here's the AppSettings property:

  1. public static NameValueCollection AppSettings
  2. {
  3.     get
  4.     {
  5.         if (DateTime.Now.Subtract(_refreshTime).Minutes > 15) /* refresh every 15 minutes */
  6.         {
  7.             try
  8.             {
  9.                 _syncRoot.EnterReadLock();
  10.                 ConfigurationManager.RefreshSection("appSettings");
  11.                 _refreshTime = DateTime.Now;                        
  12.             }
  13.             finally
  14.             {
  15.                 if (_syncRoot.IsReadLockHeld)
  16.                     _syncRoot.ExitReadLock();
  17.             }
  18.         }
  19.  
  20.         return ConfigurationManager.AppSettings;
  21.     }
  22. }


This is basically a wrapper around ConfigurationManager.AppSettings collection, with one caveat: it invokes ConfigurationManager.RefreshSection("appSettings") method in thread-safe manner every 15 minutes or so (of course, 15 minutes isn’t written in stone).

What this means, is that on the average, the updated value for configuration key will propagate to the AppDomain in 7.5 minutes. (0 minutes in best case scenario, 15 minutes in worst case. If we trust uniform distribution of random access - the average is 15/2 = 7.5).

Here's a compressed version of all this:

Derived Configuration Class
  1. public class SomeApplicationConfiguration : BaseConfiguration
  2. {
  3.     public static int SomeNumericConfigurationValue
  4.     {
  5.         get
  6.         {
  7.             return GetValueFromConfiguration<int>("SomeNumericConfigurationValue") != (default(int))
  8.                        ? GetValueFromConfiguration<int>("SomeNumericConfigurationValue")
  9.                        : 10000;
  10.         }
  11.     }
  12. }

Base Class
  1. public class BaseConfiguration
  2. {
  3.     private static ReaderWriterLockSlim _syncRoot = new ReaderWriterLockSlim();
  4.     private static DateTime _refreshTime = DateTime.Now;
  5.  
  6.     public static NameValueCollection AppSettings
  7.     {
  8.         get
  9.         {
  10.             if (DateTime.Now.Subtract(_refreshTime).Minutes > 15) /* refresh every 15 minutes */
  11.             {
  12.                 try
  13.                 {
  14.                     _syncRoot.EnterReadLock();
  15.                     ConfigurationManager.RefreshSection("appSettings");
  16.                     _refreshTime = DateTime.Now;
  17.                 }
  18.                 finally
  19.                 {
  20.                     if (_syncRoot.IsReadLockHeld)
  21.                         _syncRoot.ExitReadLock();
  22.                 }
  23.             }
  24.  
  25.             return ConfigurationManager.AppSettings;
  26.         }
  27.     }
  28.  
  29.     public static T GetValueFromConfiguration<T>(String key)
  30.     {
  31.         var typeConverter = TypeDescriptor.GetConverter(typeof(T));
  32.  
  33.         if (AppSettings.AllKeys.ToList().Contains(key))
  34.             return (T)typeConverter.ConvertFromInvariantString(AppSettings[key]);
  35.         else
  36.             return default(T);
  37.     }
  38. }

 

That about does it. Happy coding!

Posted on Wednesday, August 15, 2012 5:18 PM | Back to top

Copyright © Strenium | Powered by: GeeksWithBlogs.net