Tuesday 19 April 2011

Don’t use ConfigurationManager!

.Net is generally has a nice API but something's are problematic when the classes are static, like FormsAuthentication or ConfigurationManager, allowing developers to use them anywhere in the code.

If you’re one of those dev’s that slaps a ConfigurationManager.AppSettings in the middle of a class then stop, please just stop.  I know that it works but the side effect is that you’re creating a hidden dependency that dev’s who come along afterwards to maintain the code know nothing about and then get bitten in the ass when they try to change something (yes I know you should look through the code for things like this but I was in a hurry ok?)

To be clear, I believe that settings should be injected into classes that need them rather than having to provide a whole object to handle it (think of a connection string) but if you're refactoring old code and already have the dependency then its good to be able to start off by being able to see the dependency rather than have it hidden.

I know what you’re going to say “but its a static class so I don’t have any other way to use it”, but that’s where you’re wrong and with a little work you can have dependency injected, unit tested code loveliness and in fact I’ve done the work for you all you have to do is reuse it!

So what’ve I done? Well quite simply just written a wrapper around the ConfigurationManager’s static methods that allow you to use it via an interface and inject it into any classes that may need it, lets look at some code to see it a little clearer.

Here we have an absurdly simple example that demonstrates that just looking at the public interface of this class you’d have absolutely no idea that it uses ConfigurationManager internally:
   1: public class SomeClass
   2: {
   3:     public void SomeMethod()
   4:     {
   5:         int value = int.Parse(ConfigurationManager.AppSettings["TheValue"]);
   6:     }
   7: }

At least with the code above you may go investigating “what lies beneath” and find the hidden dependency but what if you came across this:

image

My guess is like me you’d anticipate that the class had been sorted for dependency injection and only needed the 2 dependencies that its constructor was advertising and it wasn’t until you got bitten by a problem that you’d look at the code and find:


   1: public class SomeClass
   2: {
   3:     private readonly IDependency1 _firstDependency;
   4:     private readonly IDependency2 _secondDependency;
   5:  
   6:     public public SomeClass(IDependency1 firstDependency, IDependency2 secondDependency)
   7:     {
   8:         _firstDependency = firstDependency;
   9:         _secondDependency = secondDependency;
  10:     }
  11:  
  12:     public void SomeMethod()
  13:     {
  14:         string theSetting = ConfigurationManager.AppSettings["TheValue"];
  15:  
  16:         theSetting = theSetting + "ArbitaryValue";
  17:  
  18:         _secondDependency.TheSetting = theSetting;
  19:  
  20:     }
  21: }


So the solution to both of these problems is to inject the ConfigurationManager into the clas and to do enable us to do that step forward IConfigurationManager.

The code is written in .Net 4.0 but should work with .Net 2.0 and above if recompiled to target whatever version of the framework you are working with.  For brevities sake I’m just going to show the second code snippet using IConfigurationManager which makes the code look like this;


   1: public class SomeClass
   2:    {
   3:        private readonly IConfigurationManager _configurationManager;
   4:        ..... 
   5:  
   6:        public SomeClass(IConfigurationManager configurationManager, IDependency1 firstDependency, 
   7:                         IDependency2 secondDependency)
   8:        {....}
   9:  
  10:        public void SomeMethod()
  11:        {
  12:            string theSetting = _configurationManager.AppSettings["TheValue"];
  13:  
  14:            theSetting = theSetting + "ArbitaryValue";
  15:  
  16:            _secondDependency.TheSetting = theSetting;
  17:  
  18:        }
  19:    }


Now anybody looking at the public interface for this class can see that it has a dependency on IConfigurationManager which in turn should indicate that the code is going to be reading from a App or Web.config. The only difference to the actual code using the configuration setting is the use of the instance variable rather than the static class.

The actual IConfigurationManager project contains the implementations of both ConfigurationManager and WebConfigurationManager (plus tests) to show that you can use IConfigurationManager to replace either of these classes. Currently only the more common methods & properties have been implemented as following the principle of YAGNI I’ve not bothered adding methods such as OpenExeConfiguration that I don’t need at the moment.

The one extra thing that I have added is the ability to be able to return a strongly typed custom configuration section rather than retrieving a section as an object and then casting it, so instead of :


   1: var sect = ConfigurationManager.GetSection("sampleSection")as SampleSectionProvider;

You can do:

   1: var sect = _configurationManager.GetSection<SampleSectionProvider>("sampleSection");


I know there’s not a lot of difference but I prefer the generic version since an error will be thrown if it can’t get the appropriate section handler that’s been specified.

One word of warning the Core project has a dependency on System.Web this isn’t a problem for me as I’m mostly using this with web projects but if you wanted to use this with a desktop app you will need to either remove the class from the project or except the assembly dependency.

If you are using ConfigurationManager then I strongly recommend trying out IConfigurationManager, if not for you then for the person who has to maintain the code in a few months time Winking smile

1 comment:

  1. Hey! I decided to compile and reference your DLLs in my project. I'd like to share them via nuget. Are they published? Mind if I publish them for you? I know there are other choices on Nuget but I went with yours.

    ReplyDelete