MEF is a component of .net framework 4.0, to create
lightweight, extensible applications. It avoid hard dependencies and lets
application developer to discover and use extensions without any configuration
required.
Why MEF?
Imagine a situation where an application is using several
smaller components. And 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 application. But this approach too has
drawback. As 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 having 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 Initialization
As 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 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 (Lazyi in operations)
{
if (i.Metadata.Symbol.Equals(operation))
result= i.Value.Operate(left, right).ToString();
}
I had 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 “\Extensions”
folder.
No comments:
Post a Comment