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.