…or “How to resolve stuff all over the place” – two appropriate titles for a blog post that shows the solution to a problem you will eventually run into when using an IoC container… if not in your first application, then maybe in the next…
What’s the problem?
Not all applications lend themselves equally well to leveraging an IoC container for resolving stuff. Generally, applications that follow some kind of request-response pattern are easy to adapt to use the container to build objects that handle requests: Just resolve some kind of handler that handles each request.
E.g.: It is trivial to make Castle Windsor resolve/release controllers in an ASP.NET MVC application by implementing a
CastleWindsorControllerFactory.
There are applications however, that do not follow this kind of pattern – or at least not as clearly. Take for instance a typical Windows Forms desktop application that looks like this:
|
public static void Run() { // yield control to winforms event loop... Application.Run(new MainForm()); } |
An attempt to use the container could look like this:
|
public static void Run() { using(var container = new WindsorContainer().Install(FromAssembly.This())) { var mainForm = container.Resolve<MainForm>(); // yield control to winforms event loop... Application.Run(mainForm); container.Release(mainForm); } } |
But then one problem remains: How can we make the Windows Forms application use our beloved Windsor Container without resolving the entire world at this point?
E.g. if
MainForm at some point needs to show a
SubForm, then how do we do that without doing a
new SubForm() inside of
MainForm, thus tying
MainForm directly to the concrete
SubForm? (and worse: ruining our ability to unit test
MainForm…?)
Let’s try using a service locator
In order to avoid
new’ing up
SubForm, we could use a service locator to provide the form to us. Consider this button handler:
|
void ButtonOpenSubFormClicked(object sender, EventArgs ea) { var form = ServiceLocator.Resolve<SubForm>(); form.Show(); } |
That solves the problem, right? Wrong! I mean, now
SubForm can have dependencies injected and stuff, but
MainForm is tied to a class called
ServiceLocator with a static generic method that creates stuff.
That’s pretty hard to unit test, and in the long run this will just prove to be a serious PITA.
How to fix the problem
If we wanted to fix the problem with
MainForm and
SubForm, we could do it by passing a
ISubFormFactory to
MainForm like so:
|
public interface ISubFormFactory { SubForm CreateSubForm(); } public class MainForm { readonly ISubFormFactory subFormFactory; public MainForm(ISubFormFactory subFormFactory) { this.subFormFactory = subFormFactory; } } |
thus allowing
MainForm to instantiate
SubForms at will without depending directly on the container. Now on to implement
ISubFormFactory:
|
public class UncoolSubFormFactory : ISubFormFactory { public SubForm CreateSubForm() { // oh noes not again..!!1: return ServiceLocator.Resolve<SubForm>(); } } |
Now, why is that implementation uncool? There are several reasons, and two of them are: 1) I’m tying myself to a Windsor container somewhere in a static variable in my app, and 2) The implementation is stupid and boilerplate – I need to write one of these every time I need to show a new form… Uncool!
Enter
TypedFactoryFacility
Windsor comes with a bunch of facilities in the box, and one of them fits this gap nicely:
<a href="http://stw.castleproject.org/Windsor.Typed-Factory-Facility.ashx">TypedFactoryFacility</a>.
If
TypedFactoryFacility had a tagline it would be either “The missing link!” or “Gimme another chance!” or something like that.
Put shortly,
TypedFactoryFacility gives the container the ability to dynamically implement the
ISubFormFactory interface from above, thus allowing my code to pull stuff from the container without even knowing it! \o/
In order to do that, I need to start out by registering the facility;
|
container.AddFacility<TypedFactoryFacility>(); |
and then I need to register my factory service like so:
|
container.Register(Component.For<ISubFormFactory>().AsFactory()); |
Now the container will delegate the call to
CreateSubForm() to
container.Resolve<SubForm>(), based on the return type of the factory method – nifty!
When working with Windsor, it is very important that everything that has a transient lifestyle gets properly released! – the facility has this capability as well, just implement a method with the following signature on the factory:
void Release(SubForm subForm).
Now our
MainForm’s event handler can look like this:
|
void ButtonOpenSubFormClicked(object sender, EventArgs ea) { var form = subFormFactory.CreateSubForm(); form.Closed += delegate { subFormFactory.Release(form); }; form.Show(); } |
allowing Windsor to properly take care of decommission concerns, thus properly disposing
IDisposables and more.
I hope this post gave an understandable introduction to
TypedFactoryFacility. To help some more, I have created a simple Windows Forms application on GitHub that shows how a simple Windows Forms application can implement a simple MVC pattern using Castle Windsor and
TypedFactoryFacility.
Comments, questions and suggestions are appreciated 🙂