Wednesday, March 14, 2012

Using Autofac's Auto-Factories to Avoid Service Locator

An autofac-style factory delegate
An autofac-style factory delegate
(credit: pasukaru76)

I recently switched a project's DI solution from StructureMap to Autofac.

The reason was because the only way I could get StructureMap to handle dynamic instantiation was to use a variant of Service Locator, which couples your code to your container, making unit tests impossible and reducing code coverage.

In this article I'll explain the problem with StructureMap, and how Autofac solved it elegantly.

I'll explain the problem using a Something class, i.e. any class you might want to dynamically instantiate somewhere in your code. Here it is in all it's glory:

public class Something : ISomething
{
    public Something(int anArgument)
    {
        //an implementation
    }
}

How it works in StructureMap
One of the biggest problems with using StructureMap (as mentioned in this post and this post), is that your entry-point to the container is via the static class ObjectFactory, which always resolves to a singleton container.

Commonly you'll see a Bootstrapper class like this one below, where all the dependencies are registered (explained here):

public static void Bootstrap()
{
    ObjectFactory.Initialize(x =>
    {
        x.For<ISomething>().Use<Something>();
        x.For<IAnotherThing>().Use<AnotherThing>();
    }
}

The main benefit of the static reference is that when you need to dynamically instantiate an instance later in your code, you can use the static class again to refer to the same container (rather than 'newing up' an instance of the container, which wouldn't have all of your existing bootstrapped mappings):

public class SomethingClient
{
    public void SomeDynamicScenario(int anArgument)
    {
        var something = ObjectFactory
            .With("anArgument").EqualTo(anArgument)
            .GetInstance<ISomething>();
        //operate on new object
    }
}

The downside of this benefit is that it's a Service Locator. The method can't be unit tested, because when the test runner gets to the ObjectFactory, it doesn't know what an IService is. You could tell it by running the Bootstrapper in your test, but then ofcourse then you aren't unit-testing any more.

In other words, your code is coupled to the StructureMap container.

The standard approach
The standard approach to making this testable is to extract your instantiation logic into an ISomethingFactory. That way, you can inject the factory and mock it in your tests.

public class SomethingClient
{
    private readonly ISomethingFactory somethingFactory;

    public SomethingClient(ISomethingFactory aSomethingFactory)
    {
        somethingFactory = aSomethingfactory;
    }

    public void SomeDynamicScenario(int anArgument)
    {
        var something = somethingFactory.Create(anArgument);
        //operate on new object
    }
}

public class SomethingFactory : ISomethingFactory
{
    public ISomething Create(int anArgument)
    {
        return ObjectFactory
            .With("anArgument").EqualTo(anArgument)
            .GetInstance<ISomething>();
    }
}

But then ofcourse, all you are doing is moving the problem somewhere else. How would you unit test SomethingFactory? You can't.

The problem is that wherever you have 'peppered' container references around your code (i.e. ObjectFactory references), you won't be able to test and your implementation will be coupled.

At that stage you could just say, well, that's the boundary of our unit tests - we will adopt a policy where we just don't unit test factories. And there's merit in that, because you are defining a unit test boundary and everything on one side of it will be unit tested. You could still test the factory as a module, or not test it at all. But then again...

Step in Autofac
The good thing about Autofac is, not only does it provide a solution which gives you 100% code coverage, it's also actually much easier to implement.

You can just declare your factory as a delegate method (which returns an ISomething), include it as a constructor argument and that's it! Autofac will work out from your ISomething mappings to build and inject a factory for you, saving you the bother of having to even define one in the first place.

Here's the SomethingClient example again:

public class SomethingClient
{
    private readonly Func<int, ISomething> somethingFactory;

    public SomethingClient(
                  Func<int, ISomething> aSomethingFactory)
    {
        somethingFactory = aSomethingfactory;
    }

    public void SomeDynamicScenario(int anArgument)
    {
        var something = somethingFactory(anArgument);
        //operate on new object
    }
}

//No need for a factory class

When the container instantiates SomethingClient it discovers the Func delegate. The delegate has a return type that Autofac is registered to handle, and a parameter list Autofac recognises as ISomething constructor parameters, and so Autofac injects a delegate that acts as a factory for us.

The Func delegate in this example takes one int argument, but ofcourse you can plug in any useful combination of input parameters.

Mixed constructor argument parameters
Now, this all works fine when we have a simple parameter with just a single int, but what happens when we update ISomething to accept a mixture of dependencies and value-type arguments?

public class Something : ISomething
{
    public Something(int anArgument,
                      Point aPoint, IDependency aDependency)
    {
        //an implementation
    }
}

In this example let's assume that the Point above is a type you have defined in your codebase to represent a value type, and therefore you want to specify this value dynamically on construction rather than by registering it with the container:

public struct Point
{
    public int X;
    public int Y;
    public Point(int anX, int aY)
    {
        X = anX;
        Y = aY;
    }
}

On the other hand ofcourse, IDependency is a dependency and you want that injected for you, by the container. How do we declare our factory?

We only include the value types and we omit the dependency:

public class SomethingClient
{
    private readonly 
              Func<int, Point, ISomething> somethingFactory;

    public SomethingClient(
              Func<int, Point, ISomething> aSomethingFactory)
    {
        somethingFactory = aSomethingfactory;
    }

    public void SomeDynamicScenario(
                   int anArgument, Point aPoint)
    {
        var something = somethingFactory(anArgument, aPoint);
        //operate on new object
    }
}

//No need for a factory class

No IDependency mentioned anywhere! Autofac is smart enough to allow you to add dependencies to the underlying class constructor's signature, without changing the factory, and it will still inject the dependencies for you!

Which is exactly what you want - as your solution grows and you find yourself adding dependencies, your factories do not change. They are created for you and extra dependencies are included on instantiation transparently.

This type of joined-up thinking makes Autofac a very nice container to work with. Lots of things happen for you, right out of the box, based on convention - you just have to know which and when.

The composition root
As this post eloquently explains, there should be only one place in an application where your container is referenced - the composition root. In this location you should do everything necessary to resolve the entire object graph - i.e. Register, Resolve, Release.

By using a static reference to a singleton container, StructureMap encourages these rules to be broken. Autofac, on the other hand, provides a container that implements IDisposable, which means you can use a using block to enforce the RRR pattern:

public class Program
{
    public static void Main()
    {
        var containerBuilder = registerAssemblyTypes();

        using (var container = containerBuilder.Build())
        {
            //single entry point
            var entryPoint = 
                    container.Resolve<ICodebaseEntryPoint>();
            //start using entryPoint
        }
    }

    private static ContainerBuilder registerAssemblyTypes()
    {
        var programAssembly = Assembly.GetExecutingAssembly();
        var builder = new ContainerBuilder();

        //perform auto-wiring
        builder.RegisterAssemblyTypes(programAssembly)
            .AsImplementedInterfaces();

        return builder;
    }
}

Component discovery
While we're here, take a look at the call to ContainerBuilder.RegisterAssemblyTypes (Assembly). This little call uses reflection to look at your concrete classes and your interfaces, and work out which go with which. It uses a convention over configuration approach, and you can include clauses and overrides to filter and specify particular instantiations.

StructureMap also provides a similar component discovery model.

In fact, I'm sure StructureMap can do most of the things Autofac can do one way or another (see this article on avoiding static references), and I've used StructureMap successfully many times. It's just that Autofac does things nicely by default.

Broadening the discussion about relationships
One final point - Nicholas Blumhardt, the creator of Autofac, has included a very nice chart on his blog describing different object relationship types (of which dynamic instantiation via factories is one):

Relationship Adapter Type Meaning
A needs a B None Dependency
A needs a B at some point in the future Lazy<B> Delayed instantiation
A needs a B until some point in the future Owned<B> Controlled lifetime
A needs to create instances of B Func<B> Dynamic instantiation
A provides parameters of types X and Y to B Func<X,Y,B> Parameterisation
A needs all the kinds of B IEnumerable<B> Enumeration
A needs to know X about B before using it Meta<T> and Meta<B,X> Metadata interrogation

It's nice to consider contextually where the problem we've just dealt with sits. For more discussion on these types, check out Nicholas' introductory article.

1 comment:

  1. Nice post! The only thing I might add to this is that the decision to use Service Location for resolving your dependencies is an architectural decision taken by the developer, independent of the DI container being used. In the same way that you can use Autofac to register your dependencies at the entry point to your application, you can do the same with StructureMap. Likewise, you can use Autofac for Service Location if you really want to. The advantage that using a DI container gives you over resolving the dependencies yourself is only apparent if you don't use Service Location, as you've highlighted in your article. I do agree that Autofac is generally more elegant in its approach.

    ReplyDelete

Note: Only a member of this blog may post a comment.