Dec 22 2011

Inversion of Control Containers - Things Every Senior .NET Developer Should Know, Part 3

Category: ThingsYouShouldKnowMatt @ 12:47

If you tell me you’re a senior .NET developer, there’s a handful of assumptions I’m going to make about what you know.  The “Things Every Senior .NET Developer Should Know” series of posts will look at those things.  Today’s topic will cover Dependency Injection, Inversion of Control containers, and my favorite Inversion of Control container, StructureMap.

A World Without IoC

Many brownfield applications are written in a very procedural fashion.  These applications typically have very large classes with methods containing hundreds of lines of logic.  Such systems are hard to maintain.  Striving for object-oriented code can help alleviate some of these problems, but not all of them.  Even in a system where responsibilities are broken up into multiple classes, a single class will likely “new up” instances of other service classes that it needs in order to complete its work. This creates very tight coupling, violates numerous SOLID principles, and ensures that the system will be neither easy to test nor to maintain.  A test against any class will also cover all of the class’s dependencies.  We need a way to break these hard dependencies on concretions so that we can depend completely on abstractions.

Dependency Injection… or Inversion of Control?

There is a lot of mixed-up information on the web about Dependency Injection and Inversion of Control.  Many people think that Dependency Injection is one of the SOLID principles (it’s not).  The confusion stems from how the terms have been used to describe everything from architectural characteristics, to design patterns, to specific implementations.  There’s a lot of bickering over the terminology and the underlying concept that gets in the way of the discussion.  So, let’s settle on this simplified definition for the purposes of this article:

Dependency Injection – A design pattern by which an instance of a class is given references to services it needs to fulfill its responsibilities.

Dependency Injection addresses numerous architectural and implementation issues that arise in software development.  It is one way to implement the Dependency Inversion principle and to reduce coupling in your application.  By applying the pattern, your code will become easier to test, easier to reuse, and easier to debug.  Applied correctly and with the support of a good container (as discussed in the next section), you can create loosely-coupled, cohesive systems that are easier to maintain than a system that does not utilize the pattern. 

Some argue that Dependency Injection is merely a specific type of Inversion of Control. From the perspectives of this article, let’s just assume that’s true. Inversion of Control containers in .NET implement the Dependency Injection pattern, as defined above.  Let’s take a look at the basic features of IoC containers in .NET.

Inversion of Control Containers

There is no shortage of Inversion of Control containers available for .NET, and any of them will help you to implement the Dependency Inversion pattern in your system.  Here are a few of the more popular ones:

  • StructureMap
  • Ninject
  • Castle Windsor
  • Unity
  • Spring.NET

All these containers provide the same basic capabilities, though how they provide these capabilities varies somewhat from container to container.  All IoC containers provide some mechanism for registering services and retrieving services.  Most are also capable of managing the lifecycle of services they create.  Some provide advanced registration capabilities, enabling you to register types from one or more assemblies based upon a set of conventions. 

Since the containers are mostly equivalent in terms of basic features, choosing a container largely comes down to preference.  My personal favorite is StructureMap. It’s one of the faster IoC containers, and it also has one of the richest feature sets in the box.  It’s DSL is easy to use, it ships with a nice set of conventions to get you up and running, and it’s quite easy to extend with additional conventions of your own. With it’s type scanning capabilities, nested containers, and excellent debugging information, there’s very little you can’t do with it.  From here on, we’ll look at what you can do with StructureMap, but keep in mind that most of what I’m going to show you is probably doable with any of the major containers.

Basic Service Registration

The predominant way to configure StructureMap is using it’s fluent Domain Specific Language.   StructureMap has a static ObjectFactory that can serve as the main container for your application, but if you wish, you can create your own instance of the Container class directly.  Here’s a very simple example of registering a couple of services:

ObjectFactory.Initialize(x =>
{
    x.For<IOrderProcessor>().Use<StandardOrderProcessor>();
    x.For<IInventory>().Use<InventoryService>();

    x.For(typeof (IRepository<>)).Use(typeof (NHibernateRepository<>));
}

As your application requirements grow, you will probably want to break up your configuration to keep things manageable.  This is where StructureMap’s Registry class comes in.  A registry allows you to specify configuration in a compositional way.  I typically create a registry per feature or major component in my applications.  The above example could be broken up to utilize registries instead:

public class OrderRegistry : Registry
{
    public OrderRegistry()
    {
        For<IOrderProcessor>().Use<StandardOrderProcessor>();
        For<IInventory>().Use<InventoryService>();
    }
}

public class NHibernateRegistry : Registry
{
    public NHibernateRegistry()
    {
        For(typeof(IRepository<>)).Use(typeof(NHibernateRepository<>));
    }
}

ObjectFactory.Initialize(x =>
{
    x.AddRegistry<OrderRegistry>();
    x.AddRegistry<NHibernateRegistry>();
}

Retrieving Services

StructureMap, like all IoC containers, allows you to retrieve services that have been registered.  Here’s an example of requesting a service using ObjectFactory.  Notice how we can request a closed generic type (the customer repository) even though we only registered the open generic type.

var processor = ObjectFactory.GetInstance<IOrderProcessor>();
var customers = ObjectFactory.GetInstance<IRepository<Customer>>();

StructureMap also allows multiple types to be registered for a single service interface.  You can then retrieve all instances using the container’s GetAllInstances method:

foreach (var task in ObjectFactory.GetAllInstances<IRunAtStartup>())
{
    task.Execute();
}

Note that using ObjectFactory  in this manner is actually the Service Locator pattern.  You should minimize the places where you are requesting services directly in this manner, and instead rely on whatever hooks are exposed for whatever platform you’re developing for.  For example, in an ASP.NET MVC application, you can register StructureMap as your dependency resolver, and it will take care of creating your controllers, views, etc.   Similar patterns can be implemented for WinForms, WebForms, and WCF services as well.

Dependency Injection

Here’s where one of the major benefits of using an IoC container comes in: when you request a service from the container, the container will locate and provide any dependencies that the requested service requires. It does this recursively, so if ServiceA depends on ServiceB, and ServiceB depends on ServiceC, then retrieving an instance of ServiceA will also cause the container to resolve ServiceB and ServiceC. If you request a service whose dependencies cannot be resolved, the container will throw an exception.  

Containers support two types of dependency injection: constructor injection and property injection.  Constructor injection is the preferred approach.  To take advantage of it, simply create a constructor for your type that accepts any required dependencies.  In the example below, our order processor requires a repository as well as an E-mail dispatcher. 

public class OrderProcessor : IOrderProcessor
{
    public OrderProcessor(IRepository<Order> orders, IEmailDispatcher dispatcher) 
    {
        ...
    }
    ...
}

The order processor could create these dependencies itself, but that would introduce very tight coupling.  By leveraging constructor injection, we introduce less coupling while also improving testability. We can reconfigure the container to provide different implementations for these dependencies at runtime, and we can inject mock or stub objects to facilitate unit testing (<shamelessPlug>or you could use something like SpecsFor to take care of that step for you</shamelessPlug>). 

Lifecycles

Services registered with StructureMap can be assigned a particular lifecycle.  By default, services are given a “Transient” lifecycle, which means every call to the container for a particular service will cause the container to create a brand new instance of that service.  StructureMap supports several other lifecycles out of the box, such as Singleton (the same instance will always be returned), ThreadLocal (all requests for the service on a particular thread receive the same instance), and HttpContext (all requests for the service for the duration of a single request will receive the same instance).  You can also define your own custom lifecycle by implementing the ILifecycle interface.  This example shows how to use StructureMap lifecycles in implementing the session-per-request pattern for NHibernate:

public class NHibernateRegistry : Registry
{
    public NHibernateRegistry()
    {
        For<ISession>().HttpContextScoped().Use(NHibernateBootstrapper.GetSession);
        ...
    }
}

Note that the configured lifecycle also applies any time a service is created by StructureMap, including when the service is created in order to satisfy a dependency of another service that is being created.

Scanning and Conventions

Most of the major containers are capable of everything covered so far, but not all of them have the same robust support for type scanning and custom conventions.  By leveraging these capabilities, you can collapse tedious boilerplate code like this:

public class ServiceRegistry : Registry
{
    public ServiceRegistry()
    {
        For<IServiceA>().Use<ServiceA>();
        For<IServiceB>().Use<ServiceB>();
        For<IServiceC>().Use<ServiceC>();
        For<IServiceD>().Use<ServiceD>();
        For<IServiceE>().Use<ServiceE>();
        For<IServiceF>().Use<ServiceF>();
        //And so on...
        ..
    }
}

into a single call via StructureMap’s scanner DSL:

public class ServiceRegistry : Registry
{
    public ServiceRegistry()
    {
        Scan(scan =>
                {
                    scan.TheCallingAssembly();
                    scan.WithDefaultConventions();
                });
    }
}

That’s just the tip of the iceberg though.  You can do advanced things, such as implement an Application Bus, by leveraging these same principles. Here’s an example that automatically registers all event handlers in any assembly whose name begins with “LazyDeveloperGroup:”

public class MessagingRegistry : Registry
{
    public MessagingRegistry()
    {
        Scan(scanner =>
                {
                    scanner.AssembliesFromApplicationBaseDirectory(assembly => assembly.FullName.StartsWith("LazyDeveloperGroup."));
                    scanner.ConnectImplementationsToTypesClosing(typeof (IHandle<,>));
                    scanner.ConnectImplementationsToTypesClosing(typeof (IHandle<>));
                });
    }
}

With that bit of registration, event handlers for any type of event can be retrieved from the container:

var newUserHandler = ObjectFactory.GetInstance<IHandle<NewUserEvent>>();
var loggedInHandler = ObjectFactory.GetInstance<IHandle<UserLoggedInEvent>>();

The scanner is a very powerful feature of StructureMap, and the examples I’ve shown are just the tip of the iceberg.  You can even take full control over the scanning process by providing your conventions.  For more information, check out the (somewhat stale) scanning docs.

Nested Containers

Another excellent feature that sets StructureMap apart is baked-in support for nested containers, also known as child containers.  A nested container is created from a parent container as  shown in this example (borrowed from Jeremy Miller’s blog):

using (IContainer nested = ObjectFactory.Container.GetNestedContainer())
{
    // pull other objects from the nested
    // container and do work with those services
    var service = nested.GetInstance<IService>();
    service.DoSomething();
}

Notice how the nested container is wrapped in a using block.  This will dispose the nested container when execution leaves the block, and that’s where something cool happens: any transient instances created by the container will also be disposed.  With a normal container, the caller is responsible for disposing of any services once its finished with them.  This means your code must be aware of which services require disposal, which complicates things and can muddy up your core logic with irrelevant concerns.  By leveraging a nested container and correctly disposing of it at logical boundaries (for example, at the end of an HTTP request), you don’t have to worry about what does and doesn’t require disposal.

One great use for nested containers is in implementing patterns such as NHibernate’s session-per-request or session-per-method-call patterns.  Jeremy Miller also has a good example illustrating how Dovetail utilizes nested containers.

Decorators

Embracing an IoC container you are actually making it far easier to adhere to the SOLID principles.  One way that it helps you stay SOLID is by making it trivial to layer new behaviors onto existing services by applying decorators.  I’ve blogged previously about how StructureMap enabled me to add row-level security to Fail Tracker without changing any of my existing code. 

public class SecurityRegistry : Registry
{
    public SecurityRegistry()
    {
        For<CurrentUser>().Add<CurrentUser>();

        For(typeof(IRepository<Project>))
            .EnrichWith((ctx, obj) => new ProjectSecurityDecorator((IRepository<Project>)obj, ctx.GetInstance<CurrentUser>()));

        For(typeof(IRepository<Issue>))
            .EnrichWith((ctx, obj) => new IssueSecurityDecorator((IRepository<Issue>)obj, ctx.GetInstance<CurrentUser>()));
    }
}

The syntax could use a little work in my opinion (look for more on that in a future post Smile ), especially when you want to layer on more than one decorator, but it’s still a great way to add on new behavior without having to rip the guts out of your application.  Without an IoC container, using a decorator to add new behavior would require that you find everywhere that you are instantiating the underlying service, apply the decorator, and update all your existing tests.  With a container though, applying the decorator is really just a simple configuration concern. 

Wrapping Up

It’s 2011 as I write this. At this point, I really feel that all .NET developers should at least be familiar with Inversion of Control and know what an IoC container can do. If you’re not, then you need to spend a little time discovering what they’re all about. IoC containers provide too many benefits to not use one.  I use them even for small projects because I’ve found that I’m able to build an application faster with a container than I am without one.  If you want to call yourself a senior developer but aren’t familiar with IoC, don’t worry, this article has covered a lot of the basics already.  If you want to give StructureMap a try in your next project, just run ‘Install-Package StructureMap’ from your NuGet Package Manager Console and see for yourself how much easier it is to create loosely-coupled applications.

Coming up next in the “Things Every Senior Developer Should Know” series, we’ll look at a practice that easily quadrupled my productivity: Test-Driven Development.

Tags:

blog comments powered by Disqus