Sponsored By Aspose - File Format APIs for .NET

Aspose are the market leader of .NET APIs for file business formats – natively work with DOCX, XLSX, PPT, PDF, MSG, MPP, images formats and many more!

Beyond web.config

I recently needed a configuration mechanism which would detect changes without requiring an application domain restart. I also wanted to move away from XML. This is what I came up with (and hopefully I’ll get some helpful feedback).

First, we declare a ConfigurationData object which holds our actual configuration values:

public interface IConfigurationData
{
   bool LogAll{ get; }
   string CdnUrl{ get; }
}

public class ConfigurationData : IConfigurationData
{
   private bool _logAll;
   private int _cdnUrl;
   
   public bool LogAll
   {
      get { return _logAll; }
      set{ _logAll = value; }
   }
   public string CdnUrl
   {
      get { return _cdnUrl; }
      set { _cdnUrl = value; }
   }
}

There isn’t much to explain here, so let’s move on to the class that does the heavy lifting:

public static class Configuration
{
   private static readonly string _applicationPath = HttpRuntime.AppDomainAppPath;
   private static ConfigurationData _instance = LoadInitialConfiguration();

   public static IConfigurationData GetInstance
   {
      get { return _instance; }
   }

   private static ConfigurationData LoadInitialConfiguration()
   {
      var watcher = new FileSystemWatcher(_applicationPath, "settings.config");
      watcher.NotifyFilter = NotifyFilters.LastWrite;
      watcher.Changed += (s, e) => _instance = LoadConfiguration();
      watcher.EnableRaisingEvents = true;
      return LoadConfiguration();
   }
   private static ConfigurationData LoadConfiguration()
   {
      return Converter.DeserializeFromFile<ConfigurationData>(_applicationPath + "settings.config", "_");
   }        
}

The class essentially loads the ~settings.config file, and sets up a watch to reload it whenever it changes. Here I’m using JSON (and my own JSON library) to read the file, but you could use anything, such as yaml or xml. The file might look something like:

{
   "logAll": true,
   "_cdnUrl": "http://cdn.mysite.com/"
}

If you are using a DI framework, such as ninject, you can hook it via:

Bind<IConfigurationData>().ToMethod(c => Configuration.GetInstance);

otherwise, you can call it directly from code with:

if (Configuration.GetInstance.LogAll) {...}

There are two things you’ll need to be careful with. First you’ll want to make sure to avoid caching values within a class. For example, if you did something like:

public static class HtmlExtensions
{
   private static readonly string _cdnUrl = Configuration.GetInstance.CdnUrl;
}

then a copy of CdnUrl would be stored in _cdnUrl and changes to your file wouldn’t be available to this class.

Secondly, the Changed event is known to fire twice. This shouldn’t cause any threading issues, but if your LoadConfiguration is particularly intensive, or if you happen to be debugging something, then you’ll at least know to expect it.

This entry was posted in Uncategorized. Bookmark the permalink. Follow any comments here with the RSS feed for this post.

20 Responses to Beyond web.config

  1. Bob Cravens says:

    I liked your concept and have extended it a tiny bit. I have posted code here:

    http://codepaste.net/sarjon.

    The configuration management has been refactored into its own class. This allows the implementation of a configuration manager that reads the web.config or another file (you provide your own parsing routine).

    The advantage to me of this code is it is reusable. I am a bit uneasy with the static class. I implemented the analogous class, but will probably not use it. Getting config data from static classes has the disadvantages for testing. For instance, you need to read / write to the file system for testing. I will probably instantiate a config object and then cache it.

  2. rtpHarry says:

    I have been working with FileSystemWatcher in a Windows Service today. I posted up a little mechanism to get around that double event firing problem. I probably shouldn’t share it here as it doesn’t have any interfaces or design patterns in it :)

    See the bottom Community Content entry at http://msdn.microsoft.com/en-us/library/system.io.filesystemwatcher.aspx

    I basically have a static DateTime on my class and discard any additional events if it happens less than a second after the first one.

  3. I’ve been playing around with using MongoDB to handle my configuration settings. The fluidity of the object database and low friction of the C# API are nice.

    However, the service itself needing to run is a little painful.

  4. karl says:

    Bob, for some cases there may be advantages to doing something more clever with the path. For simpler cases, I think this is a fine first approach.

    I find JSON readable, but that may depend on the amount and type of information you need. YAML or XML may be preferred by others.

    Good think you caught the static class, ‘cuz I was about to apologize for not having a private constructor :)

  5. Bob Cravens says:

    …just saw that the whole class is ‘static’ so that takes care of #1 from my previous response…

  6. Bob Cravens says:

    General concept looks great. What about the following…

    1. You are allowing users to ‘new’ up an instance of the ‘Configuration’, when it looks like you intend to have a singleton. If so, make the ctor private.

    2. The config file path is tightly constrained to the a certain file in a web app. I have had projects where there are multiple config files. I would prefer to see this path injected into the design. Or at least able to override. This may add design pressure that breaks your ability to keep the objects / methods static. I think that is okay. You can still instantiate an instance and then cache the non-static object.

    3. Generally config files are considered to be never touched by a person. However they are. So my preference would be to keep the config file as verbose as possible to add clarity. JSON is great for transport because it really strips out a lot of the XML tags. However, for clarity…maybe not so good.

  7. Steve Py says:

    Just be sure that the exception handling is solid. The trouble with config is it’s “configurable”… Someone screwing around with the production settings should ideally not take down the app if you can avoid it. (Try to deserialize, if failed, log it, e-mail it, keep the current settings, and try again later.)

  8. Instead of filesystem watcher, you can keep the singleton instance in asp.net cache with cache dependency of the actual web settings file itself.
    If any changes to your settings file will invalidate the cache. While reading the singleton websettings you can look for the existence of the cache, if cache exists use the existing singleton instance else recreate the singleton instance.

    By doing above, you don’t need to rely of file system watcher.

  9. ms440 says:

    Karl,

    Cool as always, thanks!

    Just a reminder, though: FileSystemWatcher won’t work in Medium Trust :( .

    I think Phil was using App_Code folder for that…

    Anyway, thanks man!

  10. karl says:

    @brad
    The code should be fixed. In ConfigurationData, change:

    public int CdnUrl
    {
    get { return _cdnUrl; }
    set { _cdnUrl = value; }
    }

    to

    public string CdnUrl
    {
    get { return _cdnUrl; }
    set { _cdnUrl = value; }
    }

    (int changed to string)

  11. Brad Buntin says:

    Karl, Looks pretty cool. I’m recieving this error though:

    ‘BeyondWebConfig.ConfigurationData’ does not implement interface member ‘BeyondWebConfig.IConfigurationData.CdnUrl’. ‘BeyondWebConfig.ConfigurationData.CdnUrl’ cannot implement ‘BeyondWebConfig.IConfigurationData.CdnUrl’ because it does not have the matching return type of ‘string’.

    Looks like the CdnUrl property in the ConfigurationData class needs to be of type string and not int.

  12. Heiner Wolf says:

    Interesting. When I was fed up with the XML mess, I used C# as config “language” (instead of our JSON). C# makes the config file a C# script with all the language features, conditional programming, etc. See my blog post “Scripting Configuration for C#” http://blog.wolfspelz.de/2009/10/scripting-configuration-for-c.html

  13. Rei Roldan says:

    Isn’t this what Cache and CacheDependency are for?

  14. Ravi Terala says:

    I’ve always used WCF (and XML) deserialization to read configuration files and never parse it by hand. Lot less code to maintain.

    Some changes you might want to consider: You can make the Configuration class non-static and have it injected to its uses through DI.

  15. Rob says:

    One of the guys here speaks highly of http://genuilder.codeplex.com

    Might be worth considering as a better way to generate your strongly typed classes.

  16. Rob says:

    Very nice, I like it. Clever idea deserializing.

    Don’t think I have the time to implement such a thing. We’re still on xml, I’ve written a DSL using ruby to handle all the config variables for us, and place them in the *.config file.

    Works ok for us. And our xml configs aren’t too bad to look over (at the moment).

  17. karl says:

    @Jeremy:
    I’ve considered running my own Timer, say on a 1 minute interval, and checking the file’s last write time specifically because of issues with the FileSystemWatcher. Might actually do it.

  18. Jeremy Gruenwald says:

    I did something fairly similar to this a couple of years ago, and ran into a problem where the FileSystemWatcher simply failed to fire sometimes, for no apparent reason. After doing a lot of research, I found a number of reliable sources saying that the FileSystemWatcher is unpredictable, and shouldn’t be solely relied upon. Computers aren’t random, so I’m sure there’s an underlying cause, but I could never find a pattern.

    Since my app was already exposed as a web service, I ended up writing a little utility method so I could manually kick off a load of the new data if the watcher didn’t fire. Just something you might want to consider.

  19. karl says:

    The SystemClock idea is great for testing purposes, so that you can inject values into otherwise random static calls (like DAteTime.Now and Random.Next), but I don’t see how it’d be applicable to something like changing runtime configurations.

  20. Henning Anderssen says:

    What about doing something like Jimmy Bogard’s SystemClock? Not sure if it would work, but check it out anyways.

    http://www.lostechies.com/blogs/jimmy_bogard/archive/2008/11/09/systemtime-versus-isystemclock-dependencies-revisited.aspx