There still seems to be some confusion about what is Dependency Injection (DI) and what is a DI Container, so in this post I will try to sort it out as explicitly as possible.

DI is a set of principles and patterns that enable loose coupling.

That's it; nothing else. Remember that old quote from p. 18 of Design Patterns?

Program to an interface; not an implementation.

This is the concern that DI addresses. The most useful DI pattern is Constructor Injection where we inject dependencies into consumers via their constructors. No container is required to do this.

The easiest way to build a DI-friendly application is to just use Constructor Injection all the way. Conversely, an application does not automatically become loosely coupled when we use a DI Container. Every time application code queries a container we have an instance of the Service Locator anti-pattern. The corollary leads to this variation of the Hollywood Principle:

Don't call the container; it'll call you.

A DI Container is a fantastic tool. It's like a (motorized) mixer: you can whip cream by hand, but it's easier with a mixer. On the other hand, without the cream the mixer is nothing. The same is true for a DI Container: to really be valuable, your code must employ Constructor Injection so that the container can auto-wire dependencies.

A well-designed application adheres to the Hollywood Principle for DI Containers: it doesn't call the container. On the other hand, we can use the container to compose the application - or we can do it the hard way; this is called Poor Man's DI. Here's an example that uses Poor Man's DI to compose a complete application graph in a console application:

private static void Main(string[] args)
{
    var msgWriter = new ConsoleMessageWriter();
    new CoalescingParserSelector(
        new IParser[]
        {
            new HelpParser(msgWriter),
            new WineInformationParser(
                new SqlWineRepository(),
                msgWriter)
        })
        .Parse(args)
        .CreateCommand()
        .Execute();
}

Notice how the nested structure of all the dependencies gives you an almost visual idea about the graph. What we have here is Constructor Injection all the way in.

CoalescingParserSelector's constructor takes an IEnumerable<IParser> as input. Both HelpParser and WineInformationParser requires an IMessageWriter, and WineInformationParser also an IWineRepository. We even pull in types from different assemblies because SqlWineRepository is defined in the SQL Server-based data access assembly.

Another thing to notice is that the msgWriter variable is shared among two consumers. This is what a DI Container normally addresses with its ability to manage component lifetime. Although there's not a DI Container in sight, we could certainly benefit from one. Let's try to wire up the same graph using Unity (just for kicks):

private static void Main(string[] args)
{
    var container = new UnityContainer();
    container.RegisterType<IParser, WineInformationParser>("parser.info");
    container.RegisterType<IParser, HelpParser>("parser.help");
    container.RegisterType<IEnumerable<IParser>, IParser[]>();
 
    container.RegisterType<IParseService, CoalescingParserSelector>();
 
    container.RegisterType<IWineRepository, SqlWineRepository>();
    container.RegisterType<IMessageWriter, ConsoleMessageWriter>(
        new ContainerControlledLifetimeManager());
 
    container.Resolve<IParseService>()
        .Parse(args)
        .CreateCommand()
        .Execute();
    container.Dispose();
}

We are using Constructor Injection throughout, and most DI Containers (even Unity, but not MEF) natively understands that pattern. Consequently, this means that we can mostly just map interfaces to concrete types and the container will figure out the rest for us.

Notice that I'm using the Configure-Resolve-Release pattern described by Krzysztof Ko┼║mic. First I configure the container, then I resolve the entire object graph, and lastly I dispose the container.

The main part of the application's execution time will be spent within the Execute method, which is where all the real application code runs.

In this example I wire up a console application, but it just as well might be any other type of application. In a web application we just do a resolve per web request instead.

But wait! does that mean that we have to resolve the entire object graph of the application, even if we have dependencies that cannot be resolved at run-time? No, but that does not mean that you should pull from the container. Pull from an Abstract Factory instead.

Another question that is likely to arise is: what if I have dependencies that I rarely use? Must I wire these prematurely, even if they are expensive? No, you don't have to do that either.

In conclusion: there is never any reason to query the container. Use a container to compose your object graph, but don't rely on it by querying from it. Constructor Injection all the way enables most containers to auto-wire your application, and an Abstract Factory can be a dependency too.


Comments

MEF also supports constructor injection natively. See [ImportingConstructor] in the MEF documentation section "Declaring Imports".
2010-09-01 16:29 UTC
Yes, MEF supports it, but I chose my words pretty carefully. Any container that needs a special attribute to handle Constructor Injection can hardly be said to understand the pattern. I can't point MEF to a class that uses Constructor Injection and expect it to natively recognize it as such. MEF understands the [ImportingConstructor] attribute, but that's a different thing altogether.
2010-09-01 17:34 UTC
I have to agree with Mark.

Having special constructors all over the code makes MEF kind of lame in comparison to .NET NInject or Java's CDI.
I am sure it works just fine, but seems a bit too "Hand's on" when there are a few good options that require less customization on host containers.
2012-03-02 16:53 UTC


Wish to comment?

You can add a comment to this post by sending me a pull request. Alternatively, you can discuss this post on Twitter or Google Plus, or somewhere else with a permalink. Ping me with the link, and I may add it as a comment.

Published

Monday, 30 August 2010 20:06:58 UTC

Tags



"Our team wholeheartedly endorses Mark. His expert service provides tremendous value."
Hire me!