Some time ago I developed a small chunk of code which enabled Unity and Managed Extensibility Framework to consume each others components (see my posts here and here). After a while I hooked up with @TheCodeJunkie who was, and still is, responsible for managing MefContrib project, which aims at delivering high quality MEF extensions developed by the community. Soon, my integration layer became a part of that library! Yeah, I was excited =) After a while, I posted a refined version. Basically, I extracted some generic code from the existing Unity integration stuff which could be reused with other DI containers. So in this post I want to discuss a simple adapter for Castle Windsor which is built using that infrastructure and enables MEF to consume components registered in Windsor container.
Implementation
All the required stuff lives in MefContrib.Containers namespace (MefContrib.dll assembly) which has been renamed from MefContrib.Integration as the new name is more meaningful. There is one interface and one ExportProvider which are interesting in this scenario. IContainerAdapter is an interface which encapsulates basic behaviour of a typical IoC container. The ContainerExportProvider class then uses that interface to extract relevant components from the IoC and provides them to MEF. So the only part missing from the equation is the actual IContainerAdapter implementation for Windsor Container. Unsurprisingly, it is very simple. Here it comes:
public class WindsorContainerAdapter : ContainerAdapterBase
{
private readonly WindsorContainer _container;
public WindsorContainerAdapter(WindsorContainer container)
{
if (container == null)
{
throw new ArgumentNullException("container");
}
_container = container;
_container.Kernel.ComponentRegistered += ComponentRegisteredHandler;
}
private void ComponentRegisteredHandler(string key, IHandler handler)
{
RegisterCastleComponent(handler);
}
public override object Resolve(Type type, string name)
{
return name == null
? _container.Resolve(type)
: _container.Resolve(name, type);
}
public override void Initialize()
{
var handlers = _container.Kernel.GetAssignableHandlers(typeof (object));
foreach (var handler in handlers)
{
RegisterCastleComponent(handler);
}
}
private void RegisterCastleComponent(IHandler handler)
{
var name = handler.ComponentModel.Name;
var type = handler.Service;
// By default, Windsor assigns implementation's full name for the key,
// but for a default key we want to pass null instead
if (handler.ComponentModel.Implementation.FullName == name)
{
name = null;
}
OnRegisteringComponent(type, name);
}
}
All we have to do is to inform ContainerAdapterBase that a component has been registered within the container so that the ContainerExportProvider knows which types are available. In the Initialize method we have a chance to register components which were registered in the IoC container before the adapter had a chance to intercept this information itself. The Resolve method will be called by MEF in order to get a component from IoC.
Usage
The usage is pretty straightforward. Let’s assume we have IFoo service which maps to Foo implementation. Here’s the code which registers these types within Castle Windsor and then IFoo is consumed by MEF.
var windsorContainer = new WindsorContainer();
var provider = new ContainerExportProvider(new WindsorContainerAdapter(windsorContainer));
var compositionContainer = new CompositionContainer(provider);
// Setup Castle Windsor
windsorContainer.Register(Component.For<IFoo>().ImplementedBy<Foo>());
var fooExport = compositionContainer.GetExport<IFoo>();
Of course, named registration is supported. Also MEF can resolve all IFoo implementations if it happens that IFoo is mapped to more than one implementation (thanks to named registration). See all the tests available in the provided solution.
Where can I find this ?
This code with some NUnit tests is available as a sample for the MefContrib project. You can find its sources at https://github.com/MefContrib/MefContrib-Samples.
Enjoy!
1 comment:
How about the [ImportingConstructor], Ive tried and doesnt really work. How can i get a repository in a part injected in its constructor?
Post a Comment