Quite regularly I get asked by people how they should use IoC container in their application. I don’t think I can answer this question once and universally because every application is different, every application has different needs and every application has different opportunities for leveraging an Inversion of Control container.
However there are some general rules and patterns that I use and I thought I will blog about this instead.
While I use a concrete example of Castle Windsor, the discussion here is universal.It applies to all containers.
Inversion of Control means container does not exist
Basic difference between an Inversion of Control framework and any other kind of framework is that the control gets inverted. Your application does not call to the framework. Instead the framework is aware of your application and it goes and does its stuff with your application’s objects.
Since Inversion of Control Containers are Inversion of Control frameworks that paradigm applies to them as well. They control objects in your application. They instantiate them, manage their lifecycle, invoke methods on them, modify them, configure them, decorate them, do all sorts of stuff with them. The main point here is – the application is completely unaware of that. I feel tempted to put another Matrix analogy here, but hopefully you get the point without it.
The most visible manifestation of this fact, which clearly illustrates the lack of any knowledge about the container is that I tend not to reference the container in the application at all. The only place where the reference to the container does appear is the root project which only runs the application. It however contains no application logic and serves merely as application entry point and container bootstrapper.
Not referencing the container at all serves a few purposes. Most importantly it helps to enforce good OOP design, and blocks the temptation to take shortcuts and use the container as Service Locator.
Three calls pattern of usage
Now, probably the most interesting part is this simple bootstrapper. How do I interact with the container?
I tend to use pattern of three calls to the container. Yes you heard it right – I only call the container is three places in the entire application* (conditions apply, but I’ll discuss this below in just a moment).
My entire interaction with the container usually looks like this:
1: var container = BootstrapContainer();
3: var finder = container.Resolve<IDuplicateFinder>();
4: var processor = container.Resolve<IArgumentsParser>();
5: Execute( args, processor, finder );
The three steps are:
- Bootstrap the container
- Resolve root components
- Dispose the container
Let’s go over them in turn:
One Install to rule them all…
Bootstrapping is incredibly simple:
1: private IWindsorContainer BootstrapContainer()
3: return new WindsorContainer()
4: .Install( FromAssembly.This() );
The most important rule here (which is important in Windsor, but good to follow with other containers that enable this) is – to call Install just once and register and configure all the components during this single call. Also important stuff is to use the Install method and Installers to encapsulate and partition registration and configuration logic. Most other containers have that capability as well. Autofac and Ninject call it modules, StructureMap calls it registries.
As you can see on the screenshot above I usually have a dedicated folder called Installers in my bootstrap assembly where I keep all my installers. I tend to partition the installers so that each one of them installs some cohesive and small set of services. So I might have ViewModelsInstaller, Controllers Installer, BackgroundServicesInstaller, LoggingInstaller etc. Each installer is simple and most of them look similar to this:
1: public class ArgumentInterpretersInstaller : IWindsorInstaller
3: public void Install(IWindsorContainer container, IConfigurationStore store)
5: container.Kernel.Resolver.AddSubResolver(new ArrayResolver(container.Kernel));
This partitioning helps keep things tidy, and by leveraging Installers you end up writing less code, as now Windsor will autodiscover them and register in just a single call to FromAssembly.This(). Also another noteworthy fact about this, is that you leverage Inversion of Control principle to configure the container itself. Instead of passing the container around, which should always raise a read flag, you’re telling Windsor – configure yourself. Nice and tidy.
Another important thing to notice, is that I tend to leverage convention based registration, rather than registering all the components one by one. This greatly cuts down the size of your registration code. It takes the burden of registering each newly added components manually off of your shoulders. It also enforces consistency in your code, because if you’re not consistent your components won’t get registered.
* yes – I do interact with the container in the installers, so I clearly break the three calls rule, right? No – I interact with the container in objects that extend, or modify the container itself – in this case it’s obviously not a bad thing.
…and in one Resolve bind them
Similar to unit tests principle – one logical assert per tests, I follow the rule of allowing explicit call to resolve in just one place in the entire application. Usually this will be just one call, that pulls the root component (Controller in MVC application, Shell in WPF application, or whatever the root object in your app is). In the example above I have two root objects so I have two calls to Resolve. That’s usually OK. However if you have more than three, you might want to take a closer look at reasons for that, as it’s quite unlikely you really need this.
The important thing is to have the Resolve calls in just this one single place and nowhere else in your application. Why that’s important? To fully leverage container’s potential, instead of telling it at every step what it should do. Let it spread its wings.
It is important to let the container clean up after itself, when its done doing its job. In this case I can not only say that this is something I do. You also always should dispose your container at the end of your application. Always, no exceptions. This will let the container to shutdown gracefully, decommission all the components, give them chance to clean up after themselves, and free all the resources they may occupy.
What about you? How do you use your container?
06-20-2010 2:48 PM