Should I use MEF with an IoC container? - Part 1
In my post "Should I use MEF for my IoC needs" we took a look at the question of whether or not it is appropriate to use MEF instead of a traditional IoC container. In this post we'll discuss the question of whether or not you should use both an IoC container and MEF in the same application.
MEF and IoC container concerns
As we've mentioned in the past, MEF's primary design in V1 is around open-systems whose components (parts) are dynamically discovered at runtime. IoC containers on the other hand are primarily designed for decoupling a closed (fixed) set of application components in order to improve the maintainability of the system by the engineering team. In other words each is addressing a specific set of concerns. So if that is the case then is it plausable to use both in an application? The answer is Yes. Not only that, but we specifically designed MEF with the idea of integration with other containers in mind. Below will explore one of the key-scenarios for doing this.
Enabling third-party extensibility in an IoC container-dependent application.
You have an existing customer management system that uses an IoC container such as Castle Windsor which you use in order to promote separation-of-concens and testability. The application leverages advanced Windsor facilities for integrating with NHibernate and for applying cross-cutting-concerns (AOP) such as logging, etc. The loosely coupled design of the system has proven to be greatly beneficial and has minimized the friction for developing and deploying the system through several releases.
At some point requests start pouring in from customers want to create their own extensions in order to customize the application in their specific environments. Additionally several ISVs express interest in providing third-party value-adds such as telephony integration, and scheduling. You determine that adding a third-party extensibility model to the application makes sense in order to address this opportunity. Through your research you come across MEF and realize that it provides the infrastructure you need for extensibility. However it does so in a manner that overlaps with many of the functions that Windsor is providing. What to do? Let's explore various options.
a. Migrate the existing code to use MEF and remove the IoC container completely.
One option is to consider ripping out Castle and just using MEF. Technically this sounds reasonable, however after investigation it is not feasible. There are literally thousands of components that are currently registered in the Windsor container. Moving these to MEF involves modfying all of these existing classes in order to make them discoverable by MEF, not to mention refactoring all the places in the application where the container is accessed. Because the changes are so widespread, the likelihood of introducing breakages / bugs in the system is very high. Additionally there are the various Castle facilities such as Dynamic Proxy, and NHibernate integration which MEF does not yet support, which will have to authored by the team. The cost / risk of such changes is too prohibitive, not to mention that it seems like it should be unnecessary.
b. Implement a custom extensibility mechanism built on top of the IoC container?
Another option is to author a home-grown solution. This can be a viable option depending on the needs, and the willingness to author your own solution. For simple cases you can easily facade an extensibility mechanism on top of an IoC. Once you start getting into more advanced features like lazy instantiation, metadata, recomposition and stable composition, you are in for quite a bit of work.
c. Have both the IoC and MEF both present in the same application?
The final option I want to discuss (and the one I recommend for these situations) is to use both. Use the IoC container for addressing internal application decoupling needs, and use MEF for third-party extensibiltiy needs, that is the IoC container for IoCish things and the MEF container for MEFfish things :-)
This path is appealing for several reasons.
- It works with existing codebases whether they be applications or frameworks. it does not require any significant rewrite.
- It allows leveraging the strengths of each container for the things it does best. For example leveraging MEF's metadata model for discriminating between different extensions vs the dynamic proxy support in Castle.
However, there are several caveats of this approach.
- It raises questions such as:
- Which component (part) should live in which container?
- Who owns the lifetime?
- Can a MEF part access services coming from the IoC? How?
- Can the IoC access exports coming from MEF? How?
- It increases complexity in that now there are components living in mulitple places.
- It increases the potential points of failuire within the application.
All of these issues are managable. In my next post we'll see how.