Hosting MEF Extensions in an IoC Container
There has been a lot of discussion among IoC container users about the similarities between MEF and IoC containers. Most of this has been addressing the question - “should MEF and an IoC container be used in the same app, or are they exclusive?”
One possible answer to this question is outlined below. It isn’t by any means the definitive answer, but if these are technologies you find interesting, hopefully this article will give you plenty of food for thought.
An IoC container is not required in order to use MEF. Unless you’re building or using an IoC container, this article probably won’t apply to you :).
Defining Extensibility Points
Central to this model is the ComposablePartCatalog type and its subclasses:
AssemblyCatalog and DirectoryCatalog can read set of attributes for defining extensions:
Here Export marks the Square class as an extension providing the IShape contract.
.NET 4.0 also introduces a class for assembling extensions into complete object graphs. This class is called CompositionContainer:
The role that CompositionContainer plays in a .NET 4.0 application is very similar to the role and IoC container might play in a .NET application today.
This has raised a lot of interest among IoC container users, who want to know how they can take advantage of MEF in their applications. Should CompositionContainer, for instance, replace their current IoC container, or can the two containers work together?
The answer to this question is trickier than it seems, because there are substantial problems associated with having more than one container in an IoC-driven application:
- How do the containers mutually decide that an instance’s lifetime is over?
- How is context maintained, so that the correct type gets its correct dependencies even when they’re provided by the other container?
- If the graph building operation moves backwards and forwards between two containers, how can circular dependencies be detected (without StackOverflowException!)
- How can diagnostic messages provide the ‘full picture’ when each container only knows about a portion of the graph being constructed?
(Some of this is a general problem with composing frameworks in general, and is felt today by IoC container users integrating with UI frameworks in particular. It might be called ‘competing for control’.)
The good news is that there is at least one way to get some benefits of MEF in an application that uses an IoC container without using two containers.
MEF/IoC Container Roles
Although similar in many respects, the goals of MEF and IoC containers are different, and this gives each technology unique strengths.
MEF is built for open systems – scenarios in which the complete set of components that make up an application is defined at execution-time.
This means that MEF:
- supports self-describing componentsthat carry all of the information required to use them
- makes strong guarantees about how components will be loaded and manipulated, regardless of the execution environment
- includes robust tools for finding and loading components in different media
- allows components to be queried and inspected without necessarily loading or executing them
MEF is also part of the .NET Framework, so developers can learn how to use it once and then apply these skills in all MEF-enabled applications.
IoC containers, on the other hand, have focused on highly-decoupled yet primarily closed systems. I take the definition of ‘closed’ to mean that all application components are present during integration and testing.
This makes typical IoC containers:
- highly customizable to suit the internal architecture of a specific application
- extremely flexible in the criteria for what can be used as a component
- easy to wrap around existing frameworks and legacy code
These requirements lead in different directions when designing composition technologies, but appear together in some applications.
Both MEF and typical IoC containers are flexible enough to fit to most scenarios; however, their ideal roles might be:
- define third-party extension points using MEF
- locate and load these extensions using MEF
- structure and manage the host application’s fixed architecture using an IoC container
Hosting MEF Catalogs
There are several approaches to integrating MEF and IoC containers. The one described here is probably the most powerful/complete/complex, those interested in other perspectives might start with this recent article by Patrik Hägne.
Most IoC containers, and MEF, use a layered architecture:
MEF’s composition functionality is wrapped up in CompositionContainer, and its component model in ComposablePartDefinition and related types like ComposablePartCatalog.
Autofac, an open-source IoC container [disclaimer: written by the author] follows the same typical pattern, using different types: Container and IComponentRegistration. (Funq, Linfu, Ninject, Windsor, Spring.NET, StructureMap and Unity use similar abstractions.)
Autofac and MEF used in the same application, without any integration, look like:
The crossed-out arrow in the centre reflects the barrier that we’re trying to cross: the Autofac composition process won’t include components that are composed by MEF, and vice-versa.
This kind of cross-over is frequently desirable, e.g.:
- Application components invoke extensions
- Extensions need to access services provided by the host application
The diagram below shows how this issue can be addressed without introducing the ‘two container’ problem:
Rather than hosting the MEF components in a separate container, an adapter is used so that the MEF components are presented to the Autofac container as Autofac components.
The adapter allows MEF and Autofac components to participate in the composition process at the same time – each can have dependencies satisfied by the other.
The IoC container is chosen as the ‘host’ because:
- The application components are the ones most likely to integrate deeply into the container, and since they’re IoC container components it makes sense that they are hosted in the IoC container
- IoC containers don’t provide as robust a component mode as MEF does, so hosting components from any particular container in MEF is difficult
So how does this look in practice?
Example Application: IoC and MEF
You can download a very contrived example that demonstrates many of the moving parts here. This is not production quality design, so please don’t flame me for it :)
The example is a task scheduler – perhaps you can imagine it running as a Windows service. It has two extension points: the tasks that will be run, and an output device to which notification messages can be sent.
The extension points are regular MEF parts.
The tasks export an interface and specify the interval at which they should be invoked via metadata attributes:
Notification services are similar, exporting the IMessageNotifier interface:
The container is created and Autofac services registered as usual:
An extension method, RegisterCatalog(), allows a MEF DirectoryCatalog to be supplied to the container:
Note the list of services that will be imported from the catalog into the application. Parts can still export and import other contracts, but these won’t be available to Autofac components. This keeps the extensibility points of the application clearly defined.
Similarly, Autofac services have to be explicitly marked when they will be provided to MEF components:
There’s an example of an Autofac component consuming a mix of Autofac and MEF services:
As well as MEF components consuming Autofac services:
Everything feels natural, although there are a few awkward issues surrounding the consumption of MEF metadata in Autofac services. These should go away once the MEF Export type is a framework type (it won’t feel so leaky to inject exports into Autofac components.)
MEF defines how individual components will be manipulated, not how they will be assembled into complete object graphs.
This makes sense for open systems, because regardless of how components are packaged, they should not make assumptions about their host environment beyond the contracts that their immediate dependencies supply.
When designing components that can be used in multiple applications, each component needs to be treated independently. MEF discourages thinking about catalogs of components as subsystems with a fixed configuration.
The biggest side-effect of these decisions is that while MEF components behave consistently whether hosted in MEF or another container, groups of components will probably behave differently.
MEF’s container also offers a different set of features from the typical IoC container – these may be sorely missed in highly-extensible applications, so if high extensibility is the goal the MEF container alone is likely a better choice.
It will be some time before there is much empirical data on the benefits and pitfalls of this technique – YMMV, so be sure to carefully evaluate your needs before starting down this path. Experimental is probably the appropriate tag!
.NET 4.0 and MEF are not yet released, so these techniques may not even apply to the production version.
There is a whitepaper on the MEF site describing some of the technical requirements of the approach.
Some notes on the Autofac adapter are available on the Autofac wiki.