Most of the applications we use now a days allows you to add more functionality to it by downloading and installing additional components that extend the already installed application. These additional components could be developed by the software vendor who developed the application, or by any other 3rd party vendor.
An example would the famous digital editing software Photoshop, where you can download and install filters that you can use later to add effects and/or enhance photos.
Allowing your application to be extended by installing additional components to it is not an easy task. If your application consists of multiple smaller components that should work together, the easiest way to go on achieving this is by including the source code of those components into the application, build and deploy everything together. While you can allow the components to be built separately and then integrated at build time, this will not allow you to ship additional functionality in the future, unless you want to ship a new version of the software including that functionality.
This is where the Managed Extensibility Framework (MEF) comes into play. MEF allows your application to be extended using components that are discovered and loaded at runtime without any kind of configuration.
How MEF Works
MEF is all about composition. It composes the application by combining what it calls parts. Parts are modules that are loading at runtime and made available for the application to use. A part exposes what functionality it can offer using exports, and defines its dependencies using imports.
MEF uses a “composition container” to match the imports to the exports.
Another component is the catalog, which MEF uses to discover any parts from a defined location and make it available to the application.
To demonstrate, we are going to build a very basic application. The application prints a message to a screen. The application will pass the message that needs printing to the modules that were discovered at runtime, and these modules will do the actual printing.
Our application will consist of 4 Visual Studio Projects as shown below
MEFSample: This project will be our main application, which will be responsible for loading all the parts (or modules)
Contract: This project will contain the interface that will define the contract between the main application and the parts
Part1 and Part2: These projects will implement the interface defined in the Contract project and will export their functionality to be consumed by the main application. These will be our modules.
The first thing we need to do is define the contract
public interface IPrint{void PrintMessage(string message);}We then need to implement this interface in our parts. Below is an example
[Export(typeof(IPrint))]
public class Part1:IPrint{public void PrintMessage(string message){Console.WriteLine("Part 1: " + message);
}}In the above code, you can see the attribute “Export”. This effectively means that this class exposes the functionality defined by the IPrint contract, and can be consumed by any application that requires this functionality.
At this point, our module is ready to be consumed by any application that requires it. Now we need to let our application look for and load the module. To do so we need to define a CompositionContainer and a catalog.
In our MEFSample Program class, we define the following
private CompositionContainer container;
public Program()
{AggregateCatalog catalog = new AggregateCatalog();
string appPath= System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase);
catalog.Catalogs.Add(new DirectoryCatalog("Modules"));container = new CompositionContainer(catalog);
container.ComposeParts(this);}The above code defines a new CompositionContainer. It then defines a new AggregateCatalog and adds a new DirectoryCatalog to it that points to the Modules folder. This tells the application to look for parts in that folder.
We now define what modules the application is looking for
[ImportMany]IEnumerable<IPrint> printers;Here we are saying that our program will require all modules that implement the IPrint contract. At runtime, MEF will match imports and exports that have the same contract.
At this point our program is read to use the collection of printers.
static void Main(string[] args){Program prog = new Program();
foreach (var item in prog.printers){item.PrintMessage("Hello MEF");
}Console.ReadKey();}With the above code, our application is ready to be built and run. If you run the application now as is, you will see an empty screen. This is because our modules folder is still empty. Go ahead and copy the dlls of both Par1 and Par2 projects into the modules folder. You have to create this folder inside the Debug folder of the MEFSample application. Once you do that and run the application again, you will see the output of both modules appear on the screen as follows
As you can see, using MEF it is incredibly easy to create applications that are extensible using modules and extensions.
The sample solution can be downloaded from here.