Microsoft Managed Extensibility Framework (MEF) is a framework for building applications composed of parts. By constructing an application of parts, any part can be replaced at runtime.
In order to use this framework in your project, you will need to set reference to System.ComponentModel.Composition.dll (which you can download from http://codeplex.com/MEF) and add the following statements to the top of any module with MEF code or attributes
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
The composition of MEF parts is very loosely coupled. Coupling occurs using contracts. A contract consists of an Import and one or more Exports.
An Import is a place in your application that defines a variable but does not explicitly state how that variable is implemented. Instead, a declaration is decorated with the [Import] attribute. A parameter of the [Import] attribute describes the implementation that the variable is seeking. This parameter can be a string, a type or an interface. When MEF encounters an Import, it looks for an Export that matches the Import's contract parameter.
An Export is a class decorated with the [Export] attribute. This attribute tells MEF that the class is available to satisfy any matching Import request. Just as with the [Import] attribute, the [Export] attribute accepts a parameter that is a string, a type or an interface.
For example, the following variable declaration
[Import("MyMEFString")]
string SomeString { get; set; }
defines an import with a contract "MyMEFString". That contract is satisfied by the following export
[Export("MyMEFString")]
string ThatExportedMefString
{
get
{
return "This string was provided by an MEF contract. It is from an external assembly.";
}
}
Simply declaring this contract isn't enough. We need to tell MEF to match up the imports and exports. The following steps are necessary for MEF to do its thing
1. Create a catalog. A catalog tells MEF where to find the Imports and Exports.
2. Create a container that uses the catalog.
3. Create a batch and call the Compose method of that batch. The Compose method tells MEF to find the Imports and Exports that make up each contract and match them up.
If you want to compose parts that all exist in the currently executing assembly, this is done with the following code
var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
var container = new CompositionContainer(catalog);
var batch = new CompositionBatch();
batch.AddPart(this);
container.Compose(batch);
The code above assumes that the contract's Exports and Imports are in the same project.
Notice the first line of code, in which we declare a variable named 'catalog'. This line instantiates a Catalog object. A Catalog object defines where MEF looks for parts. MEF supports the following types of Catalogs
- An Assembly Catalog retrieves MEF components compiled into a given assembly.
- A Directory Catalog retrieves all MEF components in all assemblies in a given directory.
- An Aggregating Catalog is used when you want to retrieve MEF components from multiple places - for example, from a specific assembly and from a directory; or from two different directories.
Of course it doesn't make much sense to go to the trouble of using a loosely-coupled framework like MEF and getting your parts from the current assembly. In fact, it often doesn't make sense to get parts from an assembly that is known at design time. If we know the assembly name and location, we might as well set an explicit references to it.
A more likely scenario is to import all MEF assemblies found in a specific folder. To do this, we use a Directory Catalog. We replace the declaration/initialization of the catalog with the following line
string folderToWatch = @"C:\Development\MEF\DGCode\MEFDemo1\MEFComponent1\bin\Debug";
var catalog = new DirectoryCatalog(folderToWatch);
By setting our catalog to a DirectoryCatalog, we tell MEF to search for imports in all assemblies found in a well-known folder. We can just drop DLLs into this folder and MEF will find them and use them at runtime, even if our projects do not set a reference to them.
The MEFDemo1 solution is a very simple sample that uses this code.
In MEFDemo1, a console application declares a string variable named "SomeString" which is decorated with the Import attribute and defined by the contract "MyMEFString".
At runtime, MEF determines the The value of SomeString by searching for the Export half of the contract. It finds it in ThatExportedMefString variable, declared in the Class1 class of the MEFComponent1 project. MEF knows to look here because we pointed a DirectoryCatalog at the folder where MEFComponent1.dll is compiled. (If you copy this solution to a different directory, you will need to change the path defined in folderToWatch and recompile the MEFComponent1 project. In a real-world application, we would probably pull the folder name from a configuration file or otherwise set it at runtime to make it even more flexible.)
The important point is that we don't need to set any references between these projects in order for them to communicate. The contract and MEF take care of this for us.
In this article, we showed a very simple MEF contract, based on a string. In the next article, we'll show how to create a contract based on an interface.
Code: MEFDemo1.zip (563.48 KB)
Note: The code in this article uses MEF CTP 5.