How not to do dependency injection - using XML over fluent configuration
Virtually all the developers that I come across who do not like using IoC containers have been put off by verbose, error prone XML-based configuration. Many people do not even realise that a type-safe alternative is available in all the popular IoC containers. If you are struggling with XML config, fluent configuration could be the answer to your prayers.
This is part three of a three part series on dependency injection mistakes. All the articles in the series are listed below:
- The Static or Singleton Container
- Configuring the IoC Container in Unit Test Projects
- Using XML over Fluent Configuration
This argument is not as clear cut as the previous discussions on static containers and single component resolutions because there is no doubt that there are positives and negatives for each method of container configuration. On balance though, for most situations, fluent configuration is infinitely more desirable than XML. In cases where you have one fixed implementation of each interface in your solution (which is very common), XML configuration has many drawbacks whilst offering no discernible benefits. Let's look at the two options:
IoC Container Configuration Options
The vast majority of IoC containers offer two distinct methods of registering components. You can use XML-based configuration, either as a configuration section in your web.config or as a separate config file:
<unity>
<typeAliases>
<typeAlias alias="IUpperCaseService" type="MvcApplication1.Models.IUpperCaseService,
MvcApplication1" />
<typeAlias alias="UpperCaseService" type="MvcApplication1.Models.UpperCaseService,
MvcApplication1" />
<typeAlias alias="ILowerCaseService" type="MvcApplication1.Models.ILowerCaseService,
MvcApplication1" />
<typeAlias alias="LowerCaseService" type="MvcApplication1.Models.LowerCaseService,
MvcApplication1" />
<typeAlias alias="IExampleContext" type="MvcApplication1.Models.IExampleContext,
MvcApplication1" />
<typeAlias alias="ExampleContext" type="MvcApplication1.Models.ExampleContext,
MvcApplication1" />
<typeAlias alias="HierarchicalLifetimeManager"
type="Microsoft.Practices.Unity.HierarchicalLifetimeManager,
Microsoft.Practices.Unity" />
</typeAliases>
<containers>
<container>
<types>
<type type="IUpperCaseService" mapTo="UpperCaseService" />
<type type="ILowerCaseService" mapTo="LowerCaseService" />
<type type="IExampleContext" mapTo="ExampleContext">
<lifetime type="HierarchicalLifetimeManager" />
<constructor>
<param name="prefix">
<value value="XML Config " />
</param>
</constructor>
</type>
</types>
</container>
</containers>
</unity>
The configuration registers three interfaces and their associated components. The last registration explicitly sets the component lifetime and also provides a string value that is required by the constructor. A common real-world scenario for this would be a connection string in a DAL/DataContext.
You can register the same components in code using a fluent-style syntax:
container.RegisterType<IUpperCaseService, UpperCaseService>();
container.RegisterType<ILowerCaseService, LowerCaseService>();
container.RegisterType<IExampleContext, ExampleContext>(
new HierarchicalLifetimeManager(), new InjectionConstructor("Fluent Config "));
The fluent code is a lot more concise and far more readable. Additionally, as we will learn, there are a number of other advantages.
Another method of component resolution can be found in many IoC containers which can dramatically reduce the amount of configuration required. Auto Registration can be set up to automatically register all components matching certain criteria. For example, you could specify a certain namespace, a list of assemblies, an interface name and many more. Not everyone likes this approach though and some developers prefer to see exactly what is being registered.
Advantages of fluent IoC configuration
The number one reason why I prefer fluent configuration is compile-time checking. It is incredibly easy to make typo's in your XML configuration and these will not be picked up until you run the application. The sheer size and verbosity of many configurations means that tracking down any error is a major pain. Additionally, if you rename a class within Visual Studio, you can automatically update all references in code, but with xml configuration, you will need to remember to do it manually. XML configuration is bad enough when you just have a handful of dependencies, but on larger projects where dozens of dependencies are likely, XML configuration management can be a huge headache.
I also find that XML configuration is not as flexible as the fluent interface. Some things that are harder or impossible using XML configuration but relatively simple in fluent include:
- Injecting connection strings or appsettings from the standard .NET config sections rather than putting them directly in the container config.
- Injecting custom config sections, or custom settings object that are built using appsettings or dynamic data.
- Injecting custom factories that resolve a component based on runtime data or resolve it lazily if it is expensive to create.
Advantages of XML IoC configuration
I can only come up with one advantage that XML-based configuration provides and that is the ability to change configuration without recompilation and redeployment. For some reason, being able to change interface implementations via config is very appealing to many developers, even though very few companies actually require this level of adaptability.
If you have two implementations of an interface and want to switch from Implementation A to Implementation B on the fly, without a code deployment, then with XML-based configuration, you can do this very easily. However, it is NOT difficult to do this using fluent configuration in conjunction with an appsetting, but this requires a bit of extra work up front. Given the benefits of fluent configuration, I see this as a small price to pay and would not hesitate to use fluent in all but the most dynamic of scenarios.
In an application of any size, you will have dozens of different interfaces and implementations, but how many of these components will require this level of swapability? I am guessing that the number is (and certainly should be) very small. If not, then the combinations of different implementations would mean that testing would be a nightmare. It concerns me when I hear these kind of requirements because changing the container configuration can completely change the way the application works. Doing this without a code release and associated testing is a high risk strategy. It is also symptomatic of a long-winded or unreliable deployment process. The whole argument reminds me of the situation ten years ago, when it was common practice to put lots of business logic in stored procedures because developers found it easier to update the database, rather than doing a code deployment.
Conclusion
All popular IoC containers can be configured using XML or in code via a fluent-style API. Whilst most people starting out with IoC typically choose the XML approach, then are several reason why fluent may be preferable in many cases. XML configuration can be very long-winded and error-prone and changing component names requires manual updating of the XML configuration.
In contrast, fluent is type-safe, so you will know about any typo's at compile time and refactoring of component configuration is done automatically if you change a component's name. Additionally, fluent is much more flexible. You can mix code with configuration, so for example, you can retrieve settings from config and manipulate them before injecting them into components. You can also take advantage of factory support to create components based on runtime information. XML does offer the ultimate in flexibility though and sometimes this is required. The issue that this article addresses though is the fact that many people default to XML regardless of their requirements and by doing so, they are making IoC configuration harder and more error-prone than it could/should be.
I realise that this article is very biased towards fluent configuration, but from the many projects that I have worked on or appraised, I have a hard time coming up with reasons for XML configuration in most situations. However, I know that many experienced developers do use XML and I am very keen to hear why, so please leave a comment if you disagree with any thing in this article or have something to add.
There is a serious disadvantage to using the fluent configuration: in order to compile fluent code you must have references to all the code. If I have an interface and three implementations of that interface, if I use fluent configuration I must import all three implementations. If I use XML configuration then the application is "wired up" by the XML. Only the XML has the references.
I like this set of three articles and I also agree with Jeffrey's comment with regards to the Unity container.
To his point, because the container depends on the application objects (so it can register the types) and the app objects depend on the container (so they can get injections) a conceptual circular dependency is formed.
There are ways around this circular dependency but I'd argue they are more trouble than using XML files. (For an example of a work-around see the Microsoft - Spain DDD sample app and associated doc.)
StructureMap and other containers have additional mechanisms ("auto discovery") to deal with bootstrapping the container while minimizing the use of XML files.
Even though this series of DI posts are already one and a half years old I believe they contain a lot of useful explanation about how things should be done.
However, it's not correct that config settings cannot be injected using XML. In Unity for example it's very straight forward to write your own extension that does the job for you. I frequently use an extension for appSettings and connectionString injection.
A sample can be found here:
http://www.neovolve.com/post/2010/07/05/ConnectionStringSettings-parameter-injection-in-Unity.aspx
Even though for simple projects I agree with the author on using fluent config I have also worked on a couple of projects where we leveraged interception for logging and exception handling purposes (cross-cutting concerns). It's very handy if you can just change the interception configuration on a production server to enable extra logging/tracing information in order to resolve an issue. The beauty of that is that you can do that on a component basis. I'm sure some people will argue that this could be controlled via some appSettings but that would certainly not be as simple as changing the interception configuration.
I also agree with Jeffrey McArthur's comment. I used fluent registration in one of my project for which I created a bootstrap file in the "startup" project. However even though the concrete types are referred to in the bootstrap file only, the "startup" project required reference of all assemblies that contained the concrete implementation. This leads to one more disadvantage of fluent configuration
- Exposing your concrete types inadvertently.
So now despite abstracting all your concrete implementation, you are still providing a "gateway" to direct usage and potential code-break.