Share via


Unity and the Factory Method Pattern

I’ve been playing around with Unity recently, and in particular I’ve been trying to get Unity to resolve dependencies from factory methods, and more specifically manage the lifetime of those dependencies. If you’re not familiar with the factory method pattern then check out Wikipedia.

Out of the box, Unity does support the ability to register a type that is created by a factory method using the StaticFactoryExtension class. This extension allows you to specify a delegate that creates the type, and that delegate can then call a static factory method, or even resolve another type from the container and call it’s factory method. I’m not going to cover the specifics of how to use the StaticFactoryExtension here, but pop on over to David Hayden’s blog for an example.

However, there’s a big drawback with the StaticFactoryExtension. It doesn’t allow you to specify any LifetimeManager, nor any InjectionMembers. This means that every time the factory method type is needed by a dependency, the factory method delegate is called. So you’re either forced to make your factory method manage object lifetimes, which isn’t nice, or put up with always having a new instance for every dependency. The lack of InjectionMembers also means that the factory method has to provide precisely the correctly configured object. This isn’t much of an issue for most factory methods, but it does provide more flexibility.

So, what’s the solution? Well, I was pretty sure that it was going to require me to write some kind of Unity extension, and whilst I’m comfortable with using Unity, I’d not tried writing an extension before, other than a LifetimeManager. Unfortunately for me this requires you to know a bit about how ObjectBuilder works, and that’s not a particularly well documented bit of Unity. So, armed with the Unity source I started digging. What follows is the result.

One of the first things I realized is that all I really need to do is modify the way that Unity builds objects at the stage where a specific constructor is called, and instead call a delegate to get the object instance. Everything else should stay the same. After a bit of digging I came across the UnityDefaultStrategiesExtension class, and in it’s Initialize method are the lines:

Context.BuildPlanStrategies.AddNew<DynamicMethodConstructorStrategy>(

    UnityBuildStage.Creation);

Context.BuildPlanStrategies.AddNew<DynamicMethodPropertySetterStrategy>(

    UnityBuildStage.Initialization);

Context.BuildPlanStrategies.AddNew<DynamicMethodCallStrategy>(

    UnityBuildStage.Initialization);

The bit that caught my eye was the DynamicMethodConstructorStrategy class. Looking in more detail at this class confirmed that it was responsible for generating part of a dynamic method that would actually call a constructor. This was the class I had to intercept and modify its behaviour. With this knowledge I started building my new extension, using the StaticFactoryExtension as an example.

First I defined how I wanted to configure the extension in an interface. This interface has the same method signatures that you see on IUnityContainer for the generic versions of RegisterType, just naming them RegisterFactory and excluding the ones that take a TTo type parameter. You could quite easily add the non-generic versions too, but I didn’t bother.

public delegate object FactoryMethodDelegate(IUnityContainer container);

public interface IFactoryMethodConfiguration : IUnityContainerExtensionConfigurator

{

    IFactoryMethodConfiguration RegisterFactory<T>(

                                                   FactoryMethodDelegate factoryMethodDelegate,

                                                   params InjectionMember[] injectionMembers);

    IFactoryMethodConfiguration RegisterFactory<T>(

                                                   FactoryMethodDelegate factoryMethodDelegate,

                                                   string name,

                                                   params InjectionMember[] injectionMembers);

    IFactoryMethodConfiguration RegisterFactory<T>(

                                                   FactoryMethodDelegate factoryMethodDelegate,

                                                   LifetimeManager lifetimeManager,

                                                   params InjectionMember[] injectionMembers);

    IFactoryMethodConfiguration RegisterFactory<T>(

                                                   FactoryMethodDelegate factoryMethodDelegate,

                                          string name,

                                                   LifetimeManager lifetimeManager,

                                                   params InjectionMember[] injectionMembers);

}

Next, I defined the extension class by deriving from UnityContainerExtension and implementing the configuration interface.

public class FactoryMethodExtension : UnityContainerExtension,

                                               IFactoryMethodConfiguration

{

    public IFactoryMethodConfiguration RegisterFactory<T>(

                                                          FactoryMethodDelegate factoryMethodDelegate,

                                                          params InjectionMember[] injectionMembers)

    {

        return RegisterFactory<T>(factoryMethodDelegate, null, null, injectionMembers);

    }

    public IFactoryMethodConfiguration RegisterFactory<T>(

                                                          FactoryMethodDelegate factoryMethodDelegate,

                                                          string name,

                                                          params InjectionMember[] injectionMembers)

    {

        return RegisterFactory<T>(factoryMethodDelegate,

                                                   name,

                                                   null,

                injectionMembers);

    }

    public IFactoryMethodConfiguration RegisterFactory<T>(

                                                          FactoryMethodDelegate factoryMethodDelegate,

                       LifetimeManager lifetimeManager,

                                                          params InjectionMember[] injectionMembers)

    {

        return RegisterFactory<T>(factoryMethodDelegate,

                                                   null,

                                                   lifetimeManager,

                                         injectionMembers);

    }

    public IFactoryMethodConfiguration RegisterFactory<T>(

                                                          FactoryMethodDelegate factoryMethodDelegate,

                                                          string name,

                                                         LifetimeManager lifetimeManager,

                                                          params InjectionMember[] injectionMembers)

    {

        Container.RegisterType<T>(name, lifetimeManager, injectionMembers);

        Context.Policies.Set<IFactoryMethodPolicy>(

                                                   new FactoryMethodPolicy(

                                                           () => factoryMethodDelegate(Container)),

                                                   NamedTypeBuildKey.Make<T>(name));

        return this;

    }

    protected override void Initialize()

    {

        Context.BuildPlanStrategies.Clear();

        Context.BuildPlanStrategies.AddNew<FactoryMethodConstructorStrategy>(

                                                   UnityBuildStage.Creation);

        Context.BuildPlanStrategies.AddNew<DynamicMethodPropertySetterStrategy>(

                                                   UnityBuildStage.Initialization);

   Context.BuildPlanStrategies.AddNew<DynamicMethodCallStrategy>(

                                                   UnityBuildStage.Initialization);

    }

}

The two important pieces here are the Initialize method and the last RegisterFactory method. In Initialize I’m getting rid of the default BuildPlanStrategies and replacing them for my own. Actually, the only difference between these and the defaults defined in UnityDefaultStrategiesExtension is the FactoryMethodConstructorStrategy used in the creation stage, but I’ll come to that later.

In the last RegisterFactory method I register the from type as normal with the container, passing the LifetimeManager and InjectionMembers, but I also set an additional policy, IFactoryMethodPolicy, on the context. This wraps up the factory method delegate with the container into a more simple delegate that just returns an object, and is keyed using the same key that identifies the from type. This policy is used later when I need to call the factory delegate as part of building up the object. The FactoryMethodPolicy class is quite simple and just wraps the simplified delegate.

public delegate object FactoryMethodPolicyDelegate();

public interface IFactoryMethodPolicy : IBuilderPolicy

{

    FactoryMethodPolicyDelegate FactoryMethodPolicyDelegate { get; }

}

public class FactoryMethodPolicy : IFactoryMethodPolicy

{

    private readonly FactoryMethodPolicyDelegate factoryMethodPolicyDelegate;

    public FactoryMethodPolicy(FactoryMethodPolicyDelegate factoryMethodPolicyDelegate)

    {

        this.factoryMethodPolicyDelegate = factoryMethodPolicyDelegate;

    }

    public FactoryMethodPolicyDelegate FactoryMethodPolicyDelegate

    {

        get { return factoryMethodPolicyDelegate; }

    }

}

The final link in the chain is the FactoryMethodConstructorStrategy class.

public class FactoryMethodConstructorStrategy : DynamicMethodConstructorStrategy

{

    private static readonly MethodInfo FactoryMethodPolicyDelegateInvoke =

        typeof(FactoryMethodPolicyDelegate).GetMethod("Invoke");

    private static readonly MethodInfo GetFactoryMethodPolicyDelegateMethodInfo =

        typeof(FactoryMethodConstructorStrategy).GetMethod("GetFactoryMethodPolicyDelegate");

    public override void PreBuildUp(IBuilderContext context)

    {

        Guard.ArgumentNotNull(context, "context");

        if (GetFactoryMethodPolicyDelegate(context) == null)

        {

            base.PreBuildUp(context);

            return;

        }

        var buildContext = (DynamicBuildPlanGenerationContext)context.Existing;

        Label existingObjectNotNull = buildContext.IL.DefineLabel();

        buildContext.EmitLoadExisting();

        buildContext.IL.Emit(OpCodes.Brtrue, existingObjectNotNull);

        buildContext.EmitLoadContext();

        buildContext.IL.EmitCall(OpCodes.Call, GetFactoryMethodPolicyDelegateMethodInfo, null);

        buildContext.IL.EmitCall(OpCodes.Callvirt, FactoryMethodPolicyDelegateInvoke, null);

        buildContext.EmitStoreExisting();

        buildContext.IL.MarkLabel(existingObjectNotNull);

    }

    public static FactoryMethodPolicyDelegate GetFactoryMethodPolicyDelegate(IBuilderContext context)

    {

        var factoryMethodPolicy = context.Policies.Get<IFactoryMethodPolicy>(context.BuildKey);

        return factoryMethodPolicy == null ? null : factoryMethodPolicy.FactoryMethodPolicyDelegate;

    }

}

This class derives from the original DynamicMethodConstructorStrategy class whose behaviour I want to modify. I override the PreBuildUp method and check to see if there is an IFactoryMethodPolicy with a delegate defined for the build key in the context. If there isn’t I simply call the base implementation of PreBuildUp that will generate the dynamic method bits to call a constructor. However, if there is a delegate defined then I generate the dynamic method bits that call this delegate.

And once it’s all put together, that’s it! For good measure I’ve included the tests I used below. I’m certainly finding this useful in my projects, and if you do too I’d really like to hear about your experiences. Please feel free to comment on this post, even if it’s a bug you find!

 [TestFixture]

 public class FactoryMethodExtensionTests

 {

     public class TestStringFactory

     {

         private readonly string factory;

         public TestStringFactory(string factory)

         {

             this.factory = factory;

         }

         public string GetString()

         {

             return factory;

         }

     }

     public class TestStringDependency

     {

         private readonly string dependency;

  public TestStringDependency(string dependency)

         {

             this.dependency = dependency;

         }

         public string Dependency

         {

             get { return dependency; }

         }

     }

     [Test]

     public void FactoryMethodCanBeUsedToResolveTypeDependency()

     {

         var container = new UnityContainer();

         container.AddNewExtension<FactoryMethodExtension>();

         container.Configure<IFactoryMethodConfiguration>()

             .RegisterFactory<string>(c => "FactoryString");

         container.RegisterType<TestStringDependency>();

         var resolve = container.Resolve<TestStringDependency>();

         Assert.AreEqual("FactoryString", resolve.Dependency);

     }

     [Test]

     public void FactoryMethodCanUseContainerToResolveTypeToGetFactoryMethodUsedInDependency()

     {

         var container = new UnityContainer();

         container.AddNewExtension<FactoryMethodExtension>();

         container.RegisterInstance(new TestStringFactory("FactoryString"));

         container.Configure<IFactoryMethodConfiguration>()

             .RegisterFactory<string>(c => c.Resolve<TestStringFactory>().GetString());

         container.RegisterType<TestStringDependency>();

         var resolve = container.Resolve<TestStringDependency>();

         Assert.AreEqual("FactoryString", resolve.Dependency);

     }

     [Test]

     public void FactoryMethodExtensionLoadedStillResolvesNormally()

     {

         var container = new UnityContainer();

         container.AddNewExtension<FactoryMethodExtension>();

         container.RegisterType<object>();

         var resolve = container.Resolve<object>();

         Assert.NotNull(resolve);

     }

     [Test]

     public void FactoryMethodExtensionWithContainerLifetimeResolvesSameInstances()

     {

         var container = new UnityContainer();

         container.AddNewExtension<FactoryMethodExtension>();

         container.Configure<IFactoryMethodConfiguration>()

             .RegisterFactory<object>(c => new object(),

                                                   new ContainerControlledLifetimeManager());

         var first = container.Resolve<object>();

         var second = container.Resolve<object>();

         Assert.AreSame(first, second);

     }

     [Test]

     public void FactoryMethodExtensionWithDefaultLifetimeResolvesDifferentInstances()

     {

         var container = new UnityContainer();

         container.AddNewExtension<FactoryMethodExtension>();

         container.Configure<IFactoryMethodConfiguration>()

             .RegisterFactory<object>(c => new object());

         var first = container.Resolve<object>();

         var second = container.Resolve<object>();

         Assert.AreNotSame(first, second);

     }

     [Test]

     public void FactoryMethodExtensionWithTransientLifetimeResolvesDifferentInstances()

     {

         var container = new UnityContainer();

         container.AddNewExtension<FactoryMethodExtension>();

         container.Configure<IFactoryMethodConfiguration>()

        .RegisterFactory<object>(c => new object(), new TransientLifetimeManager());

         var first = container.Resolve<object>();

         var second = container.Resolve<object>();

         Assert.AreNotSame(first, second);

     }

}

Originally posted by Rupert Benbrook on the 23 of April 2009 here.