IoC libraries compared
I had just posted this to a question on StackOverflow about the differences between various IoC libraries. By the time I was done I realized I had a blog post.
I am prepping a presentation for a usergroup…as such I just when thru a bunch of them. Namely: AutoFac, MEF, Ninject, Spring.Net, StructureMap, Unity, and Windsor. I was able to get all of these to work but MEF. But I’m sure it is nothing that Glen Block couldn’t fix.
I wanted to show off the 90% case (constructor injection, which is mainly what people use an IOC for anyway). You can check out the solution here (VS2008).
As such, there are a few key differences: * Initialization * Object retrieval
Each of them have other features as well (some have AOP, and better gizmos, but generally all I want an IOC to do is create and retrieve objects for me)
Note: the differences between the different libraries object retrieval can be negated by using the CommonServiceLocator.
That leaves us with Initialization, which is done in two ways: via code or via xml configuration (app.config/web.config/custom.config). Some support both, some support only one. I should note: some use attributes to help the IoC along.
About my preference on xml vs code initialization: I started with Spring.Net using xml initialization, later switch to unity with code initialization. I don’t like xml initialization anymore. It is too easy to mess up, very error prone, not debugable, and not as testable as I would like.
On Attributes, which a few support, I’m not a huge fan — but I could be convinced otherwise down the road.
Finally on typeof: every time I had to write a typeof I threw up in my mouth a little bit. I’m sure there are reasons for type of, but since the inception of generics in .net 2.0 I don’t see the need. I’m looking at you Windsor and Spring.Net.
So here is my assessment of the differences:
( Code initialization only — with attributes). I hope you like lambdas. Initialization code looks like this:
1: IKernel kernel = new StandardKernel(
2: new InlineModule(
3: x => x.Bind<ICustomerRepository>().To<CustomerRepository>(),
4: x => x.Bind<ICustomerService>().To<CustomerService>(),
5: x => x.Bind<Form1>().ToSelf()
Also, Ninject has the coolest web site of all the containers.
(Initialization code or Xml or Attributes) Version 2.5 is also very lambda’y. All in all, this is one of my favorites. Some very interesting ideas around how StructureMap uses Attributes.
But, here is my StructureMap initialization code:
1: ObjectFactory.Initialize(x =>
3: x.UseDefaultStructureMapConfigFile = false;
Also, you can also go nearly config-less with StructureMap. This is the Attribute driven style. You litter all of your classes with the attributes Pluggable and PluginFamily. Then add a file to your project called StructureMap.config that looks like this:
1: <?xml version="1.0" encoding="utf-8" ?>
3: <Assembly Name="StructureMapConfigDemo" />
After that you are good to go. I kind of like this approach…but I feel dirty afterwards.
(Initialization code and Xml) Nice library, but xml configuration is a pain in the butt. Great library for Microsoft or the highway shops. Code initialization is easy:
1: container.RegisterType<ICustomerRepository, CustomerRepository>()
2: .RegisterType<ICustomerService, CustomerService>();
If you want to initialize via xml, here is what to look forward to:
3: <typeAlias alias="int" type="System.Int32, mscorlib" />
4: <typeAlias alias="singleton"
5: type="Microsoft.Practices.Unity.ContainerControlledLifetimeManager, Microsoft.Practices.Unity" />
6: <typeAlias alias="ICustomerRepository"
7: type="UnityConfigDemo.ICustomerRepository, UnityConfigDemo" />
8: <typeAlias alias="ICustomerService"
9: type="UnityConfigDemo.ICustomerService, UnityConfigDemo" />
14: <type type="ICustomerRepository" mapTo="UnityConfigDemo.CustomerRepository, UnityConfigDemo">
15: <lifetime type="singleton" />
17: <type type="ICustomerService" mapTo="UnityConfigDemo.CustomerService, UnityConfigDemo">
18: <lifetime type="singleton" />
19: <typeConfig extensionType="Microsoft.Practices.Unity.Configuration.TypeInjectionElement, Microsoft.Practices.Unity.Configuration">
21: <param name="dbo" parameterType="ICustomerRepository"/>
25: <type type="UnityConfigDemo.Form1, UnityConfigDemo">
26: <typeConfig extensionType="Microsoft.Practices.Unity.Configuration.TypeInjectionElement, Microsoft.Practices.Unity.Configuration">
28: <param name="customerService" parameterType="ICustomerService"/>
That was by far the largest amount of xml require to initialize any of the containers. I’m hoping Chris Tavares can fix that down the line.
Actually, my biggest gripe with Unity is the name. For all the other IoC libraries, if I google the name I get their web site as the first link. Not Unity. This make Unity the hardest to find info on.
(xml only as near as I can tell) But does everything under the sun that an IoC can do. I used to use this one, I will not any more.
Anyway, here is a sample of the xml from the app.config.
3: <resource uri="config://spring/objects"/>
5: <objects xmlns="http://www.springframework.net">
6: <object name="MyCustomerRepository" type="SpringDemo.CustomerRepository, SpringDemo" />
7: <object name="MyCustomerService" type="SpringDemo.CustomerService, SpringDemo" >
8: <constructor-arg ref="MyCustomerRepository" />
10: <object name="MyForm1" type="SpringDemo.Form1, SpringDemo">
11: <constructor-arg ref="MyCustomerService" />
Also, without the Common Service Locator, Spring.Net is not generic friendly. Another gripe, the download expands to 120 meg (you don’t need all of it). They include every single thing under the sun (.Net 1.1 and 2.0 asseblies, tutorials, extras, addons, docs, kitchen sink), which makes it a bit more daunting than it is.
(xml and code).
This one just rubs me the wrong way. No support for generics, configuration is a pain no matter what (to me). But it also does anything you could want it to do. (Edit: I’ve been getting a lot of heat from the Castle Windsor Right-Wing Conspiracy movement. So I took another look at the api — and realized I was wrong. Sorry, my bad. I’ve updated the code using a better api. Words to remember: Reflector is your friend — not ReSharper’s intellisense).
Here is a sample of in-code initialization:
1: IWindsorContainer container = new WindsorContainer();
3: container.AddComponentWithLifestyle<ICustomerRepository, CustomerRepository>("CustomerRepository", LifestyleType.Singleton);
4: container.AddComponentWithLifestyle<ICustomerService, CustomerService>("CustomerService",LifestyleType.Singleton);
Or, here is my code from my app.config:
5: service="WindsorConfigDemo.ICustomerRepository, WindsorConfigDemo"
6: type="WindsorConfigDemo.CustomerRepository, WindsorConfigDemo" />
9: service="WindsorConfigDemo.ICustomerService, WindsorConfigDemo"
10: type="WindsorConfigDemo.CustomerService, WindsorConfigDemo" />
13: type="WindsorConfigDemo.Form1, WindsorConfigDemo" />
(code and xml). Nice simple IoC library. Seems to do the basics with not much fuss. Here is how you initialize it:
1: var builder = new ContainerBuilder();
xml configuration looks like this:
2: <section name="autofac" type="Autofac.Configuration.SectionHandler, Autofac"/>
5: <autofac defaultAssembly="AutofacConfigDemo">
8: type="AutofacConfigDemo.CustomerRepository, AutofacConfigDemo"
9: service="AutofacConfigDemo.ICustomerRepository, AutofacConfigDemo" />
12: type="AutofacConfigDemo.CustomerService, AutofacConfigDemo"
13: service="AutofacConfigDemo.ICustomerService, AutofacConfigDemo" >
16: type="AutofacConfigDemo.Form1, AutofacConfigDemo"
17: service="AutofacConfigDemo.Form1, AutofacConfigDemo" >
Which also change the code initialization to read the xml:
1: var builder = new ContainerBuilder();
2: builder.RegisterModule(new ConfigurationSettingsReader("autofac"));
3: using (IContainer container = builder.Build())
Fun tidbit about this one: the main contributor for this project is a Microsoft employee — and the code is on Google Code. Why not CodePlex? I have no idea.
My final verdict (for just this minute)
If I had to choose today: I would probably go with StructureMap. It has the best support for C# 3.0 language features, and the most flexibility in initialization.
If I was in a non-opensource friendly environment, I would go with Unity (or MEF, but I have to get it to work first).