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.
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)
ReplyDeleteThis 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.
Thanks Krzystof, that's a very good point.
ReplyDeleteExcuse me if this is a little naive...
ReplyDeleteAs 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