Managed Extensibility Framework (MEF)

MEF is a component of .NET framework 4.0, to create lightweight, extensible applications. It avoid hard dependencies and lets the application developer discover and use extensions without any configuration required.

Why MEF?

Imagine a situation where an application is using several smaller components. And the application is responsible for creating and running those components.

One possible solution could be including all the components as source code in the application. But you cannot add new components without modifying the source code.

Another solution could be to provide an interface for decoupling between application and components. So the component can implement the interface and interact with the application. But this approach too has a drawback. As the application cannot discover components by itself, it must be explicitly told which components are required and loaded.

Here MEF comes into the picture. MEF provides a way to discover components via composition. A MEF component specifies both its dependencies (known as imports) and what capabilities (known as exports) it makes available.

Let's understand it with the help of an example. Suppose you are making a simple calculator application which currently supports addition and subtraction.

Creating composition container and catalogue

Composition container keeps track of which components are available for composition and what are their dependencies. It provides a way by which application can get the instance of components to be composed. We need to include System.ComponentModel.Composition in reference.

    //An aggregate catalog that combines multiple catalogs
    var catalog = new AggregateCatalog();
    //Adds all the parts found in the same assembly as the current class
    catalog.Catalogs.Add(new AssemblyCatalog(typeof(this).Assembly));
 
    //If parts are placed at some other location then adds that directory path

    //catalog.Catalogs.Add(new DirectoryCatalog(componentsDirectoryPath));

 
 
    //Create the CompositionContainer with the parts in the catalog
    CompositionContainer _container = new CompositionContainer(catalog);
 
    //Fill the imports of this object
    try
    {
        this._container.ComposeParts(this);
    }
    catch (CompositionException compositionException)
    {
        Console.WriteLine(compositionException.ToString());
   }

catalog.Catalogs.Add(new AssemblyCatalog(typeof(this).Assembly));

This adds the components from the current assembly. To add the components from some specified folder location, add a directory catalogue like:

catalog.Catalogs.Add(new DirectoryCatalog(componentssDirectoryPath));

Where componentsDirectoryPath is the path of directory where components can be found.

Import and Exports

Define an interface like it.

[Import(typeof(IOperation))]
public interface IOperation
{
    string Operate(int leftOperand, int rightOperand);
}


This interface has an attribute Import. This ImportAttribute defines that the type IOperation needs to be imported. So we implement the IOperation class, and above the class we use ExportAttribute indicating that it has the capabilities of IOperation. Also it contains ExportMetadata attribute indicating that depending on the metadata Symbol the operation is performed.

[Export(typeof(IOperation))]

[ExportMetadata("Symbol", '+')]

class Add : IOperation

{
//Implementation of IOperation
}Now we will do lazy initialization for getting objects like this:[ImportMany]
IEnumerable<Lazy<IOperation, IMetadata>> operations;Lazy initialization is used so that 
only operations that are needed are initialized. Based on the metadata it 
initializes what operation is to be performed. It contains ImportMany attribute 
because IOperation can be filled by many exports like add, subtract etc. Lazy 
InitializationAs we see we declare an IEnumerable for lazy initialization. Now 
how does it work? When we run application, it initializes the catalogue and 
creates a container. Based on the Import attribute it finds the components which 
can be filled for it. Here we have only one class Add to fill it. So operations 
will contains only single Lazy<IOperation, IOperationData> object, and that 
object will be initialized when it will be accessed first time. So based on the 
operation we will call Operate function.
 
foreach (Lazy<IOperation, IOperationData> i in operations)
    {
        if (i.Metadata.Symbol.Equals(operation)) 
                 result= i.Value.Operate(left, right).ToString();
    }I have a sample project to illustrate it. It contains 2 operations Add and Subtract in the assembly and Multiplication in a separate dll, which can be found in "<current executing assembly
path>\Extensions" folder.

Up Next
    Ebook Download
    View all
    Learn
    View all