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 constructor 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 it 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.

Comments

Avatar for Jake Jake wrote on 17 Apr 2011

I have never really thought about this before but you are absolutely right. Thx.

Avatar for Paul Hadfield Paul Hadfield wrote on 18 Apr 2011

You are completely correct, it's amazing how many unit tests suddenly become difficult or impossible because the code has a hard coded reference to configuration nested deep inside.

Avatar for Oded Oded wrote on 18 Apr 2011

Well said - I don't think I have worked in a single place that isn't guilty of the above and have also contributed to this sin myself in the past...

Avatar for Shawn Hinsey Shawn Hinsey wrote on 18 Apr 2011

I agree with this in principle and have been doing it for years, but one thing I'd like to point out is that your example is pretty dangerous. Instead of injecting raw types and relying on them getting the correct values because you're injecting them by name, you should create custom configuration types and take your dependencies on them instead. This can prevent really annoying errors like needing to figure out what string got injected as your connection string and where it lives.

Using this model, your general approach is:

1) In your application bootstrapper, pull your config settings out of their runtime store (app.config, whatever) and set up a configuration type.
2) Register this type in the container
3) Use it by taking a dependency on it

Avatar for Paul Hiles Paul Hiles wrote on 19 Apr 2011

@Shawn - Thanks for your feedback. Interesting perspective. I totally agree with you that a custom config is the way to go when you have multiple related configuration settings that you want to inject into a class, but I am not sure why you would go to the trouble of having a custom config section for a single connection string. Can you explain how injecting the connection string into the context as per my example is dangerous?

Avatar for Shawn Hinsey Shawn Hinsey wrote on 19 Apr 2011

In your example it's probably ultimately less problematic, but if you're doing container auto-wiring, such as you'd do with Windsor, which is what I'm most familiar with, you run into the problem that if you have more than one type with more than one string parameter, there's no easy way to differentiate them. Even with auto-wiring you can take the approach above, but I've had a couple of hair pulling days spent trying to track down a problem that ultimately boiled down to some subtle change to the configuration causing something else to be injected.

I agree that having a config type just for a connection string is probably suboptimal, so I think for them you are probably better off using the named connection string approach that the default .NET configuration system supports, but if you need to pass in other strings or ints, etc., you're probably better off creating a specific type for them.

Avatar for Peter Tran Peter Tran wrote on 20 Apr 2011

If I use DI to inject IExampleContext to my controller then I cannot use SaveChange() as part of the DbContext. Should I inject the DbContext as well? Thanks for the great article!

Avatar for Paul Hiles Paul Hiles wrote on 25 Apr 2011

@Peter - The IExampleContext interface that is implemented by the concrete context can include the SaveChanges method. You will find a lot of strong opinions regarding the question of which component should be responsible for committing the unit of work. Most people would agree that SaveChanges should not be called from within a repository class - the repository would not be aware of whether the operation it is carrying out is part of a larger unit of work. Depending on your architecture, a popular technique is for the next layer up (in this case the manager) to call SaveChanges which may suit your needs but a purist might argue that the unit of work should really be committed automatically at the end of the request. An HTTP Module is often used to facilitate this.

Avatar for Neil M Neil M wrote on 28 Apr 2011

I hope the DI/IOC crowd can guide me a little here. I am struggling with best practices (doubtless from not having a complete understanding of DI/IOC/UnitTesting), and have hit this configuration question.

I agree that deeply embedded calls to ConfigurationManager.AppSettings are not good for testing, but how do I propagate such settings down the chain of classes to such a deep level?

I intended to create an instance of a ProjectSettings poco, which gets/sets strongly typed configurable values, and initialise this as part of the app startup from App.config, and expressly in unit test setups. But making this ProjectSettings instance accessible to some deep seated class requires me to either propagate the instance through every class, or use some form of static/singleton pattern. If I use static/singleton, is this still valid during VS2010 parallel unit testing?

What is best? Bolting on an entire DI framework seems excessive for this problem.

Thanks (I hope)

Avatar for Paul Hiles Paul Hiles wrote on 11 May 2011

@Neil - I would move away from the idea of a single ProjectSettings class and just encapsulate the settings that a particular component requires. In this way, your DAL might take in a connection string, an emailer component may take in an EmailSettings class and an Amazon API wrapper might take in a AmazonCredentials class containing username, token, and securityIdentifier. You can encapsulate these as custom config sections if you desire or just build them from AppSettings when you setup your IoC container.

When used correctly, IoC does not require explicit propagation of dependencies all the way down the chain. Instead a component just has a dependency on the next level down and that in turn has dependencies of its own. So for example in MVC, your controller is the top most component. This might take in a business logic class via its constructor. The BLL class takes in a repository class, and the repository takes in an Entity Framework DbContext. Finally, the DbContext takes in a connection string. This dependency graph is automatically constructed by your IoC container when you ask for the controller. Configuration classes are injected in exactly the same way, with no need for statics or singletons.

For a more detailed overview of dependency injection, keep a look out for Mark Seemans's Dependency Injection in .NET book which will be released shortly,

Avatar for Matthew Marksbury Matthew Marksbury wrote on 20 Jan 2012

A very important point that I learned and started implementing a few years ago. It should also be noted that the configuration file is not the evil here, it's the usage of it.

For example, being able to quickly change an XML file for a production app is often necessary.

I solved this by wrapping my configuration calls inside of classes that get injected. For example, I might have an interface called IConnectionStringProvider that has a single method called GetConnectionString(). The default implementation of that would use ConfigurationManager to get the setting, but other implementations could use Environment Variables, WebService calls, or whatever approach is needed.