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 implmentation of each interface in your solution (which is very common), XML configuration has many drawbacks whilst offering no discernable 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.
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.
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 that 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.
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 are now closed for this article.