DevTrends

Configuration Settings Are A Dependency That Should Be Injected

Dependency Injection does not just apply to obvious dependencies such as repositories and logging components. It is very important to inject ALL dependencies including the less obvious ones. In my experience, one of the most overlooked areas is configuration. Many people seem perfectly happy to extract settings from config deep within their code. This is plain wrong. If you need to reference an AppSetting in your business logic, inject it. If you need a connection string in your data access code, inject that too.

I am always surprised that whilst people usually take the time to abstract out and inject services, managers, repositories and helpers, one area that is often overlooked is configuration, most commonly AppSettings and ConnectionStrings. I have lost count of the number of times I have encountered classes filled with business logic that use ConfigurationManager.AppSettings deep within them. A configuration file is a dependency. There is no doubt about it and unless you inject those configuration settings, there is no way that you can properly unit test your code.

Note that I say properly unit test, because there are of course ways to allow unit tests to run on these kinds of classes. Anyone that disagrees with the first paragraph is probably thinking 'but I can just add those settings to the app.config in the unit test project'. Yes, this will work but it is a hack. Unit tests are about testing a class in isolation. You typically inject all dependencies via the contructor and in a test, these dependencies are stubbed or mocked depending on whether you are doing state or interaction based testing (you will typically do a mixture of both for classes than have at least one dependency). Unit test projects should never have a configuration file and if they do, I am not sure that those tests should even be called unit tests. Any test that interacts with a non-mocked dependency must surely by nature be called an integration test.

So why is it a hack to add config settings to your unit test project? Well, web.config and app.config are files just like any other. So referencing configuration settings within a class means that that class has a direct dependency on the filesystem. You wouldn't feel so comfortable (i hope) if your unit test opened up a file in My Documents, or downloaded a web page from the Internet, so why do people think that is is acceptable to access web.config?

The fact is that the class under test should not care where it is getting its configuration from. It just needs these settings to carry out its functionality. Each class should have a single responsibility and that does not include knowing where to go to look up a few settings. The class should not know that the settings are coming from a file-based configuration at all. Other options such as a database or a web service are just as valid and it is perfectly possible that the configuration source may change in the future. If you inject in your settings, then such a change would have absolutely no effect on the class using the settings. The same could not be said if you were using ConfigurationManager directly.

What about connection strings?

Object relational mapping (ORM) tools such as Entity Framework and LINQ to SQL deserve a special mention because in-built dependencies in auto-generated code files are easy to miss. Both of these ORMs use a main context class. Linq to SQL uses DataContext. Entity Framework 4.0 uses ObjectContext and 4.1 can also use the new DbContext. Without user modification and when using the default constructor of the context class, both EF and Linq2Sql will read the config file in order to obtain a connection string. Fortunately, it is very simple to change this behavior. All three context classes have constructor overloads that take in a connection string, so it is very simple to modify your IoC container configuration to pass through the connection string. Here is a Unity example:

var connectionString = ConfigurationManager.ConnectionStrings["Test"].ConnectionString;

container.RegisterType<IThingManager, ThingManager>();
container.RegisterType<IThingRepository, ThingRepository>();
container.RegisterType<IExampleContext, ExampleContext>(
    new InjectionConstructor(connectionString));    

DbContext is slightly different because you subclass DbContext yourself, so additionally, you need to add a constructor to the derived class and call the base constructor.

public class ExampleContext : DbContext, IExampleContext
{
    public ExampleContext(string connectionString)
        : base(connectionString)
    {
    }

    public DbSet<Thing> Things { get; set; }    
}   

Conclusion

Ok, so this post was a bit of a rant, but I seem to see this problem in almost all of the companies I consult for so it certainly appears to be very common. In case you have not picked up on it yet, the point I am trying to make is that configuration is a dependency like any other and should be injected along with your repositories and managers. Connection strings, app settings and custom config sections are all dependencies. No exceptions.

Useful or Interesting?

If you liked the article, we would really appreciate it if you would share it with your Twitter followers.

Share on Twitter

Comments

Comments are now closed for this article.