Dependency Injection Frameworks: configuration via code

Many times I was asking myself what's better IoC container for this or that project. Their performance is the one side of choice. You can find the complete performance comparison here. Another side of the choice is the simplicity and speed to learn. So I decided to compare several from this perspective and took , Structure Map, , Unity, Castle Windsor. It seems this list is the most relevant to what can be considered as the most popular containers. You can find some of them in the list of top 20 NuGet packages and I added others by my preference. Personally, I very like Autofac and during working on this article I became firmly convinced that it's the best one in the most of practical cases.

This article describes the basics of dependency injection containers such as configuration and component registration. Next articles will compare lifetime scope management and advanced features. All code snippets can be found in the GitHub repository LifetimeScopesExamples.

Documentation Summary

During working on the article I learnt new dependency injection frameworks and good documentation helped me a lot. Unfortunately not every framework has a good one and I still needed to search for a solution in Google. This is just a summary after the article was created.

Quality Comment
Autofac Excelent All cases are described and there is no need to Google anything. It's easy to find a solution and examples are clean.
Simple Injector Good Most of the cases are described in the documentation. But still several requests to Google were done. Although it was easy to find a solution.
Structure Map Middle Not all cases are described. Registrations with expression, property and method injections are not described well. It was required to search on Google for solutions.
Ninject Poor Not all cases are described. Registrations with expression, property and method injections are not described. It was required to search on Google for solutions and they were hard to be found.
Unity Bad Despite a lot of text in documentation it's useless. All cases were searched on Google and not easy to find.
Castle Windsor Middle Not all cases are described or have good examples. It was required to search on Google for solutions.

The documentation for dependency injection frameworks:

Configuration Summary

In this article I did'not study XML configuration. All code snippets describe common usages of the dependency injection containers by means of their fluent interface. Here you can find the following:

  • Injection via Constructors, when dependencies are initialized via constructor parameters
  • Injection via Properties, when dependencies are initialized via class properties
  • Injection via Methods, when dependencies are initialized via special setter methods
  • Registration via Expressions, when you can specify additional logic on creation
  • Registration via Convention, when you can automatically register everything
  • Registration via Modules, when you can specify a class which encapsulates configuration

The purpose of the article is to bring working examples for each of the cases. Such advanced scenarios as parameterized registrations are out of the scope.

Object Model and Test Scenario

In order to test IoC containers I created a simple model. There are several modifications of it to reflect requirements for property and method injection. Some of the dependency injection frameworks require using special attributes to allow injections to properties or methods. I explicitly noticed about that in the section about registration traits.


The test scenario create a container and gets an object from it two times in order to test lifetime scope management. The later one will be described in further articles.


Dependency Injection via Constructors

The configuration to inject via constructors does not require any special attributes or naming in its basic variant.

Autofac

Simple Injector

Structure Map

Ninject

Unity

Castle Windsor

Dependency Injection via Properties

Some dependency injection containers require usage of special attributes which help to recognize properties to be initialized. I personally don't like this approach because the object model and IoC container becomes strongly tied. Ninject requires using of the attribute [Inject]Unity requires the attribute [Dependency]. At the same time Castle Windsor does not require anything to make properties initialized. They are injected by default.

Autofac

Simple Injector It does not have implicit property injection. You can use registration via expressions to initialize properties on creation.
Structure Map

Ninject

Unity

Castle Windsor

Dependency Injection via Methods

Along with the property injection the method injection can help with circular references. From the other hand, this introduces another issue which should be avoided. In few words API does not provide any hint that the execution of such method is required to make some object fully initialized.  Read more about temporal coupling.

As for the property injection some IoC containers require usage of special attributes with the same drawbacks. Ninject requires the attribute [Inject] for methods. Unity requires usage of the attribute [InjectionMethod]. All methods marked with such attributes will be executed when object is resolved.

Autofac

Simple Injector

Structure Map

Ninject

Unity

Castle Windsor

Registration via Expressions

Most of the cases for the previous topic are nothing less than registration by means of lambda expressions or delegates. Such way of registration help you to add some logic to the moment when objects are created, but it is not a dynamic approach. You should use parameterized registration in order to dynamically initialize components when resolve them.

Autofac

Simple Injector

Structure Map

Ninject

or


Unity

Castle Windsor

Ninject has differences between ToMethod and ToConstructor binding which is described here. In few words, when you use ToContructor you also may use conditional binding. The following statement won't work for ToMethod binding.


Registration via Conventions

In some cases you won't need to write a configuration code at all. The common scenario is as follows: scan an assembly for types, extract their interfaces and register them as interface-implementation pairs in the IoC container. It might be useful for really big projects, but maybe complicated for developers who is new for a project. There are several moment to remember.

autofac-autoAutofac registers all possible implementations and store them in the internal array. It uses a default one when resolve a dependency. The latest registrations will be used as per the documentation. For example, in the sample repositories with constructor initialization will be used. Simple Injector does not have any built-in method to scan and register implementation. You should do this manually. You can find the example below. Structure Map and Unity requires public implementation classes. Internal classes are not visible for their built-in scanners. Ninject requires additional package Ninject.Extensions.Conventions. The source you can find on GitHub. It also requires public implementation classes as for the previous.

Autofac
var builder = new ContainerBuilder();
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).AsImplementedInterfaces();
var container = builder.Build();
Simple Injector
var container = new Container();
var repositoryAssembly = Assembly.GetExecutingAssembly();
var implementationTypes = from type in repositoryAssembly.GetTypes()
    where type.FullName.Contains("Repositories.Constructors")
          || type.GetInterfaces().Contains(typeof (ILog))
    select type;
var registrations =
    from type in implementationTypes
    select new { Service = type.GetInterfaces().Single(), Implementation = type };
foreach (var reg in registrations)
    container.Register(reg.Service, reg.Implementation);
Structure Map
var container = new Container();
container.Configure(c => c.Scan(x => {
    x.TheCallingAssembly();
    x.RegisterConcreteTypesAgainstTheFirstInterface();
}));
Ninject
var container = new StandardKernel();
container.Bind(x => x.FromThisAssembly().SelectAllClasses().BindDefaultInterfaces());
Unity
var container = new UnityContainer();
container.RegisterTypes(
    AllClasses.FromAssemblies(Assembly.GetExecutingAssembly()), 
    WithMappings.FromAllInterfaces);
Castle Windsor
var container = new WindsorContainer();
container.Register(Classes.FromAssembly(Assembly.GetExecutingAssembly())
    .IncludeNonPublicTypes()
    .Pick()
    .WithService.DefaultInterfaces());

Registration via Modules

Modules can help you to partition your dependency injection configuration. You can group them by context (data access, business objects) or by purpose (production, tests). Some IoC containers can scan assemblies to find such modules. In the article I described the basic way of using them.

Autofac

Simple Injector It does not have anything like this.
Structure Map

Ninject

Unity

Castle Windsor

 

PS

I liked Windsor as well as Autofac though they have different default lifetime scope configuration. I'll describe that in the next article. Please, don't hesitate to propose changes to GitHub sample project or to this description. I'll reflect important moments as soon as possible.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Related Post

%d bloggers like this: