I really like MEF and I really like proper IoC containers (let the flames begin). I do not think they are mutually exclusive, but best suited for different goals. I consider MEF to be a great way to perform macro level componentization. In other words, it is a way to wire up components that are physically in different assemblies, but it is not well suited for the micro level componentization in which I need to wire up my dependencies within my application.
Glenn Block tackles this issue in a couple of blog posts. The first Should I use MEF for my general IoC needs? describes the following criteria for not choosing MEF:
- If you need/want centralized configuration of plain old CLR objects (POCOs) then MEF is not the right choice.
- We do have TypeCatalogs which allow you pick and choose which Types you have in the container, but those types still have to attributed, thus the configuration mechanism is configuring which attributed MEF parts show up. This is because our attributed model is really optimized around discovery of a set of unknown components vs configuring a pre-defined set.
- As we don’t have a central configuration mechanism, we also have no explicit way to pipe configuration settings like “Port” or “Uri” into a specific part.
- If you need AOP/Interception like injecting logging, or automatic transaction management then MEF doesn’t include any native support for this.
- Open generics support (register IRepository<> which can handle all IRepository<T> dependencies) is not baked in.
- If you need custom lifetime support such as per-thread, per-session MEF includes only two lifetime models, shared and non-shared. To get more complex lifetimes requires manually setting up container hierarchies. It is doable, but requires work.
- If you need parameterized construction, that is creating a part where specific imports are passed in at the time of construction, then this is also not supported.
I think these very clearly define the criteria for not using MEF, but more importantly, I think that most IoC containers will contains those features as part of their functional requirements. Glenn’s second article Should I use MEF with an IoC container? - Part 1 he describes three approaches to how to use MEF and IoC within the same application. To summarize his post, here is the three options:
- Replace all IoC usage with MEF
- Add an extensibility mechanism to configure the IoC, and replace all usage of MEF
- Use both in the same application, choosing the right place for each based on best fit
Option 1 is not really an option, as MEF does not really offer most of the IoC functionality that many have come to expect (some are listed above as reasons not to choose MEF). Option 2 is more feasible, and has been done for a number of IoC containers already. The problem here is that in all cases I am aware of, the implementation is somewhat of a bolt on to the IoC container. MEF does a much better job at this, and eliminates a dependency on a specific container.
This leaves option 3, which I consider to be best of both worlds. I can use MEF to assemble my components, and then use my IoC of choice to wire them up. Of course, this now leaves me the problem of bridging my two strategies into something reusable and efficient. The rest of this post is my current approach using MEF and Autofac.
The source code for this sample is posted on my github site in my samples repository as BootstrapTest. The project is built using beta 2 of Visual Studio 2010, but all the code should be usable in Visual Studio 2008. The only dependencies is MEF and Autofac, and that means you need .NET 3.5 or newer.
Ok. Now on to the source code. Here is the Program.cs source file.
{% gist dbuksbaum/4aa176cc68bb1cd0e75d Program.cs %}
Lines 12-15 configure MEF and retrieve the MEF container using a fluent interface (code to follow later in the post). Line 18 is the call to the Bootstrap.Initialize method, which returns the Autofac container. This will find all Autofac modules declared in the current assembly, and in all the assemblies located in the Extensions sub-directory.
The logic here is divided into two key parts: MEF configuration and Autofac configuration. The real value here is in the Autofac configuration, so lets look at the Bootstrap.cs source file next.
{% gist dbuksbaum/4aa176cc68bb1cd0e75d Boostrap.cs %}
There are only two lines of MEF centric code. On line 16 we inform MEF that we want to import a collection of Autofac IModule instances (see the Modules property on line 17). MEF will ensure that I have a non-null array of zero or more elements. This property gets populated by MEF on line 39, the second line of MEF centric code. Once that call completes, our use of MEF is done, and it is time to configure Autofac. That configuration takes places on lines 48-58, and is fairly basic Autofac configuration.
Line 48 creates a ContainerBuilder. Line 51 registers the Autofac ImplicitCollectionSupportModule, which is a module I always use, so I add it all the time. If you don’t want it, remove this line. Line 54 loops though all modules that MEF identifies, and line 55 registers each module with the ContainerBuilder. Finally, on line 58, we create and return the Autofac container. This is the point at which my bootstrap process is effectively completed. If you look back at line 19 in Program.cs, you will see that we use the Autofac container as we normally would use.
My Bootstrapper only requires a configured MEF container, but I didn’t like the standard configuration of MEF, so I wrapped it in a very simple fluent interface. This is not needed to bootstrap MEF and Autofac, but it is my preferred model. However, it does not support the full flexibility of MEF configuration, only the functions I tend to use frequently.
Here is the CatalogConfigurator.cs class for the fluent configuration of MEF.
{% gist dbuksbaum/4aa176cc68bb1cd0e75d CatlogConfigurator.cs %}
I only support adding the various types of MEF catalogs, and I store the configured MEF container. Subsequent calls to BuildContainer will just return the same instance, unless the rebuildContainer parameter is true. This is purely opinionated code that reflects my normal usage of MEF.
So now that we have seen bootstrapper in use, and the infrastructure code needed, lets see how we plug in a module. First, here is the interface (IStuff) I want expose through Autofac. It is located in an assembly by itself. The only thing exposed from this assembly is the IStuff interface in the following IStuff.cs source file.
{% gist dbuksbaum/4aa176cc68bb1cd0e75d IStuff.cs %}
My console application, and my implementation assembly (the plug in component), both depend on this assembly.
My implementation assembly exposed two classes. First is the implementation of IStuff, creatively named StuffA in the following StuffA.cs source file.
{% gist dbuksbaum/4aa176cc68bb1cd0e75d StuffA.cs %}
Now for the magic, the exported class that implements the Autofac module that MEF will find and plug into my console application, is in the following MyModuleA.cs source file.
{% gist dbuksbaum/4aa176cc68bb1cd0e75d MyModuleA.cs %}
The MEF code is located on line 7, and it tells MEF to export the annotated class as the type Autofac.IModule
.
That’s all the code. Physically, there is one thing left to do. I place the assembly containing my plug in module into the Extensions sub-directory. I can place it in any directory, but it has to be specified to MEF like I do on line 14 of Program.cs (see above). On that line, I tell MEF to look into the Extensions sub-directory.
Not a lot of code, but I find that it makes modularization of my applications much easier, and more importantly cleaner (a highly subjective value). Once again, the complete source code for this is located at BootstrapTest on my github site.