Tuesday, January 12, 2010

10 Advanced Windsor Tricks – 1A. A Delegate Factory Facility

Here’s part 1A of my series: 10 Advanced Windsor Tricks.

Yes, I’m getting totally side-tracked already. After I published my first trick ‘Registering Delegates’ I got into a twitter conversation with Tuna Toksoz, who, if you don’t know already, is one of the Castle committers and a seriously hardcore when it comes to IoC containers :) Tuna got me thinking that it would be very simple to extend my delegate trick to create a facility to automatically return a factory for a registered type to any class with a dependency to Func<registered type>.

This isn’t an original idea, Autofac already has ‘auto generated factories’ that work in exactly this way.

Show me the code already. OK, say we have our ThingOne from before:

public class ThingOne : IThing
{
    public string SayHello(string name)
    {
        return string.Format("ThingOne says hello to {0}", name);
    }
}

And we also have a class, UsesThingFactory that has a dependency on Func<IThing>:

public class UsesThingFactory
{
    private readonly Func<IThing> thingFactory;

    public UsesThingFactory(Func<IThing> thingFactory)
    {
        this.thingFactory = thingFactory;
    }

    public IThing GetMeAThing()
    {
        return thingFactory();
    }
}

With my AutoFactoryFacility I don’t have to register Func<IThing>, just IThing and UsesThingFactory. The facility automatically creates the resolve delegate to hand to UsesThingFactory without any further action:

var container = new WindsorContainer()
    .AddFacility<AutoFactoryFacility>()
    .Register(
        Component.For<IThing>().ImplementedBy<ThingOne>(),
        Component.For<UsesThingFactory>()
    );

var usesThingFactory = container.Resolve<UsesThingFactory>();

Console.WriteLine(usesThingFactory.GetMeAThing().SayHello("Tuna"));
Console.WriteLine(usesThingFactory.GetMeAThing().SayHello("Tuna"));

The facility only took a few minutes to knock together using a sub dependency resolver:

public class AutoFactoryFacility : IFacility
{
    public void Init(IKernel kernel, IConfiguration facilityConfig)
    {
        kernel.Resolver.AddSubResolver(new AutoFactoryResolver(kernel));
    }

    public void Terminate(){}
}

public class AutoFactoryResolver : ISubDependencyResolver
{
    private readonly IKernel kernel;

    public AutoFactoryResolver(IKernel kernel)
    {
        this.kernel = kernel;
    }

    public object Resolve(
        CreationContext context, 
        ISubDependencyResolver contextHandlerResolver, 
        ComponentModel model, 
        DependencyModel dependency)
    {
        var getResolveDelegateGeneric = GetType().GetMethod("GetResolveDelegate");
        var getResolveDelegateMethod = 
            getResolveDelegateGeneric.MakeGenericMethod(dependency.TargetType.GetGenericArguments()[0]);
        return getResolveDelegateMethod.Invoke(this, null);
    }

    public bool CanResolve(
        CreationContext context, 
        ISubDependencyResolver contextHandlerResolver, 
        ComponentModel model, 
        DependencyModel dependency)
    {
        return dependency.TargetType.IsGenericType 
            && (dependency.TargetType.GetGenericTypeDefinition() == typeof (Func<>))
            && (kernel.HasComponent(dependency.TargetType.GetGenericArguments()[0]));
    }

    public Func<T> GetResolveDelegate<T>()
    {
        return () => kernel.Resolve<T>();
    }
}

Far too much reflection voodoo for my liking. There must be a more elegant way of doing this, but I can’t see it. But in any case it works and gives me a really nice way of grabbing components as needed without any nasty dependencies on service locators or having to manually create factories.

A further refinement would be to add Func<string, T> so that you can resolve typed components by name.

3 comments:

Krzysztof Koźmic said...

I played with that idea myself, although from a different angle, which allows you to use just any delegate type (almost) (http://code.assembla.com/kkozmic/subversion/nodes/Garage/Castle.LightweightFactoryFacility?rev=78)

This approach has a limitation though - what you usually want to fetch in this way, are transient services, and by using single delegate you don't really have a way of releasing them, so if you're using default tracking policy you have yourself a memory leak. That was the main reason why I didn't publish/blog about this stuff.
You could go with pair of delegates,
Func (Resolve),Action,(Release), but at this point it's better to use typed factory facility.

Mike Hadlow said...

Thanks Krzystof, that's a very good point.

Andy Pook said...

Excuse me if this is a little naive...

As we are creating the instance via a factory then the container should not be responsible for tidying it up. It should be the responsibility of the class that used the factory.

A fuller description is at http://elegantcode.com/2008/12/14/the-component-burden/
I may be reading too much into Davy's article but it would seem to apply to this case.

His "rules" were:
- If you create an instance of a class, you are responsible for releasing/freeing/disposing it.
- If a factory creates an instance of a class, the factory is not responsible for releasing/freeing/disposing it. This burden falls upon the object which requested the object from the factory
- If you receive an instance of an object, you should not release/free/dispose it, unless you received the instance from a factory