Tuesday 21 May 2013

AutoFac Dynamic Factories

AutoFac like many Ioc containers make it easy to declare your dependencies and just get them magically injected for you. The problem comes in when trying to control the lifetime of certain objects, especially if you are trying to create some shorter lifetime objects. Once you declare your dependency you don't have any more control over the lifetime of it. Also sometimes you might specifically need 2 different instances of an object. AutoFac just like many other containers has lifetime options when registering objects:
builder.RegisterType<TestClass1>().InstancePerDependency();
builder.RegisterType<TestClass2>().InstancePerLifetimeScope();
The first indicates that each dependency or call to the Resolve() method will give you a new instance. The 2nd indicates that each dependency or call to the Resolve() within the same lifetime scope will give you a new instance. This is sort of special as it actually shares the instance in the same call graph, lifetime scope or call to container.BeginLifetimeScope(). So this registration of specific to when sharing instances is preferred.

So even if you change all your objects to InstancePerDependency lifetime (which would be hugely limiting anyway) you still need a way to create these on-the-fly instances. The AutoFac solution is to resolve an object of type Owned<T>

However this means having the reference to the IContainer in your objects which is very bad and IOC 101 dont's. So just create a simple interface for your framework that you can use to inject abstract factories instead something like:

public interface IFactory<T>
{
    T Create();
}

This is very simple and easy to generate mocks for testing. You can also easily create a generic wrapper using lambda expressions to generate on the fly even when not using AutoFac or a different container. But for the autofac version, you can call it anything you like AutoFacFactory if you like, but it will function something like this:

public class Factory<T> : IFactory<T>
{
 private IContainer _container;

 public Factory(IContainer container)
 {
  _container = container;
 }

 public T Create()
 {
  var owned = _container.Resolve<Owned<T>>();
  return owned.Value;
 }
}
In order to get this to work there's a few simple steps to configure AutoFac:

First register the open generic type:

var builder = new ContainerBuilder();
builder.RegisterGeneric(typeof(Factory<>)).As(typeof(IFactory<>)).InstancePerDependency();

Next is that you have to actually register the container instance itself back to the container, as it seems its not automatic. This can actually be done easily but you need to construct a builder to update the container:

var postContainerBuilder = new ContainerBuilder();
postContainerBuilder.Register(c => container);
postContainerBuilder.Update(container);

Now you can declare dependencies to IFactory of any type that is registered and be able to create unique instances each time:

var builder = new ContainerBuilder();
builder.RegisterType<TestClass1>().InstancePerDependency();
builder.RegisterType<TestClass2>().InstancePerLifetimeScope();
builder.RegisterGeneric(typeof(Factory<>)).As(typeof(IFactory<>)).InstancePerDependency();

var container = builder.Build();

var postContainerBuilder = new ContainerBuilder();
postContainerBuilder.Register(c => container);
postContainerBuilder.Update(container);

var factory1 = container.Resolve<IFactory<TestClass1>>();
var factory2 = container.Resolve<IFactory<TestClass2>>();

var t11 = factory1.Create();
var t12 = factory1.Create();

Assert.AreNotEqual(t11, t12);

var t21 = factory2.Create();
var t22 = factory2.Create();

Assert.AreNotEqual(t21, t22);

No comments:

Post a Comment