Monday 7 September 2009

Custom Object Factory Unity Extension

Suppose you have an object that cannot be directly created by your IoC container. In my case, it is because that object is a .NET remoting object, so it must be created on a different computer.

One way to solve this would be to register a factory that creates your object. But in my application, there are dozens of objects that need to be created in this special way, and they all inherit from a common base interface. Ideally, I would like it to be completely transparent, so I request the type I want, and the container works out that it needs to be built in a special way.

So I set about making a Unity extension, which would allow me to intercept Resolve requests for certain interfaces, and create them using my custom factory method, or return the ones already cached.

The way to accomplish this is to create a Build Strategy, which checks to see if the requested type meets our criteria. If it does, we have a look to see if we have already cached and constructed the object. If not, we call our factory method to construct it, and cache the result. One important thing to notice is that I pass the “Context” from the extension into the build strategy. That is so that if you call Resolve from a child container, it will return the same instance as if you called it from a different child container. Obviously, your requirements may differ.

The if statement in PreBuildUp contains my rule for deciding if this is a Resolve request I want to intercept. Again, this could be customised for any arbitrary logic.

public class FactoryMethodUnityExtension<T> : UnityContainerExtension
{
    private Func<Type,T> factory;

    public FactoryMethodUnityExtension(Func<Type,T> factory)
    {
        this.factory = factory;
    }

    protected override void Initialize()
    {
        var strategy = new CustomFactoryBuildStrategy<T>(factory, Context);

        Context.Strategies.Add(strategy, UnityBuildStage.PreCreation);            
    }
}

public class CustomFactoryBuildStrategy<T> : BuilderStrategy
{
    private Func<Type,T> factory;
    private ExtensionContext baseContext;

    public CustomFactoryBuildStrategy(Func<Type,T> factory, ExtensionContext baseContext)
    {
        this.factory = factory;
        this.baseContext = baseContext;
    }

    public override void PreBuildUp(IBuilderContext context)
    {
        var key = (NamedTypeBuildKey)context.OriginalBuildKey;

        if (key.Type.IsInterface && typeof(T).IsAssignableFrom(key.Type))
        {
            object existing = baseContext.Locator.Get(key.Type);
            if (existing == null)
            {
                // create it
                context.Existing = factory(key.Type);
                
                // cache it
                baseContext.Locator.Add(key.Type, context.Existing);
            }
            else
            {
                context.Existing = existing;
            }
        }
    }
}

Using the extension is very simple. Simply give it the delegate to use to create the objects, and register it as an extension:

WhateverFactory factory = new WhateverFactory();
container = new UnityContainer();
container.AddExtension(new FactoryMethodUnityExtension<IWhatever>(factory.Create));

Here’s a couple of blog posts I found helpful while trying to learn how to create a Unity extension:

No comments: