Autofac is currently my favorite IoC container with support for constructor depedency injection with things like factories with Func<T>, Func<T, params> and Lazy<T> among others. Autofac is very clean and concise with plenty of hooks throughout the lifecycle. Using Lazy<T> effectively declares that I want an instance of T that is not instantiated until I first use it. The Lazy<T> type is borrowed straight from MEF. I’m leaving a lot out here in my summary – in short it’s a great container. Whether or not it’s your container of choice, take a listen to Nikolaus Blumhardt discuss Autofac 2, MEF and other topics on “Talking Shop Down Under”. It’s a great discussion about Autofac in particular and DI in a broader sense.
But let’s take this MEF thing a little further. I should note that I am using MEF Preview 9 on .Net 3.51 from MvcContrib. This approach works the same on .Net 4.0 as well (without the need to download MEF of course).
Autofac has it’s own scanning capability that puts you closer to convention-based registration, while this approach gives you a different style of control.
Creating a Common Registrar Interface
Autofac uses the notion of a ContainerBuilder that takes our fluent configuration and roles it into a container. In that light, we’ll create a generic interface that allows us to register components via an instance of a ContainerBuilder like so:
public interface IIocRegistrarGiven this interface, we can write a concrete implementation that takes wires up our builder according to how we want things configured like so:
{
/// <summary>
/// Accepts a container builder to configure with mappings
/// </summary>
/// <param name="builder">autofac container builder</param>
void RegisterComponents(ContainerBuilder builder);
}
public class IocRegistrar : IIocRegistrar
{
#region IIocRegistrar Members
public void RegisterComponents(ContainerBuilder builder)
{
builder.RegisterType<FooModelCreator>().Named<IModelCreator>(“Foo”));
builder.RegisterType<BarModelCreator>().Named<IModelCreator>(“Bar”));
}
#endregion
}
So, that’s great, we can call that and get our container built, but what if we have components that span multiple namespaces or assemblies? Do we really want a single registrar tying all these things together?
Instead, let’s decorate that same class with the MEF ExportAttribute like so:
[Export(typeof(IIocRegistrar))]
public class IocRegistrar : IIocRegistrar
{
#region IIocRegistrar Members
public void RegisterComponents(ContainerBuilder builder)
{
builder.RegisterType<SalesRepModelCreator>().Named<IModelCreator>(“Foo”));
builder.RegisterType<AdminModelCreator>().Named<IModelCreator>(“Bar”));
}
#endregion
}
MEF in the Bloodstream - or - seeking out all registrars at runtime
This is MEF in its simplest form – we’re categorizing our Export as a type that we will use to discover at runtime. If we want to segment our containers, we can add a ‘contractName’ parameter to further declare groupings of registrars and pull them back by name. This opens up a lot of possibilities, but we’ll focus on the simplest case here.
When building up our container – we may do this in a central resolver if using ServiceLocation, or this can be done more generically on app startup and integrate it with something like Mvc’s resolver wireup. Here we’ll use the DirectoryCatalog to go after everything in the bin directory to get our wireups. If we wanted to go after registrars of a particular name, we can change the GetExportedValues call to take care of this.
var moduleBuilder = new ContainerBuilder();
var directoryCatalog = new DirectoryCatalog(GetAssemblyLocation());
// create a container that we use to discover and compose
var container = new CompositionContainer(directoryCatalog);
foreach (var registrar in container.GetExportedValues<IIocRegistrar>())
registrar.RegisterComponents(moduleBuilder);
We have now told the application to search out all IIocRegistrar exports and build up the container builder accordingly.
Resolving Instances with Autofac
From here on out, everything is the same. No traces of MEF, just Autofac container behavior as usual. Registered types can be resolved from the container created by the builder. Using Service Location to demonstrate the behavior looks like this:
var container = moduleBuilder.Build();
var modelCreator = container.Resolve<IModelCreator>("Foo");
That’s All Folks!
So there you have it. You can now have Ioc registration defined at whatever granularity you choose and even have it occur across assembly layers in your application. While Autofac supports the use of Xml configuration, the ability to test our wireup in small consumable pieces and avoid centralized registrars that reach across many namespaces keeps things light and simple.