Pages

Sunday, July 3, 2011

Managed Extensibility Framework - A Look

Hi guys,

If you are really new to .NET framework 4.0, this is going to be a quickstart guide to a new framework for extensibility which every developers should know. In this post, I will give you a brief introduction to what Managed Extensibility Framework is all about and also create a sample application on the same.

Note: This is the basic stripped version of MEF, if you are looking for something more advanced, stay tune with my blog; its about to follow. 

Why Managed Extensibility Framework?

You can say MEF is a framework that is built on top of Reflection API, that addresses one special kind of requirement that most of the current developers are in. Modularizing an application is one of  the biggest concern for any software giant. Everyone, like me, look for some sort of plugin based modularization for their respective application and ultimately end up doing in their own way like what I did before, which ultimately does not solve the problem as someone else might end up doing the same thing little differently and eventually "my plugins cannot go into your application" kind of situation. Hence we need some sort of standardization to  address this situation such that every plugin based application can work together. MEF addresses this situation by giving you a sleek way of defining your modules as plugins and your application as a Standard host of any plugins.



Lets start this using Code-First approach.

To start with MEF you need .NET framework 4.0 and you need to add an assembly "System.ComponentModel.Composition.dll" to your project. Once you are done with this, lets start writing some simple code here and explain the concept as we go.

Create a console application to host your components. Add a Class Library to the project and add an interface to it.


public interface IProcessor
{
    TimeSpan ExecutionTimeSpan { get; set; }
        
    long Process();
        
    string Message { get; set; }

    Action<string> InvertMe { get; set; }
}

The interface IProcessor is just a contract that both plugin and my application will follow. Even though you can create your application without even having an interface,  I will talk about it later, but for the time being, I have made a strict contract between the pluggable modules and the application that hosts the plugins. The interface IProcessor has few  methods in it which we need to implement somewhere. But where? Lets see.

Add a Class Library to the Solution and name it as SerialProcessor. This would add a new class to the project. Lets rename that to SerialProcess. Add reference to System.ComponentModel.Composition.dll to the project and also a reference to the Interface library.  Once you are done, lets add this code :

[Export(typeof(IProcessor))]
public class SerialProcess : IProcessor
{
#region IProcessor Members

public TimeSpan ExecutionTimeSpan
{
get;
set;
}

public long Process()
{
DateTime startTime = DateTime.Now;

long j, rem, result = 0;
for (long i = 2; i <= 5000; i++)
{
for (j = 2; j < i; j++)
{
rem = i % j;
if (rem == 0)
break;
}

if (this.InvertMe != null)
this.InvertMe(string.Format("Step [{0}, {1}]", i, j));

if (i == j)
result = i;
}

this.ExecutionTimeSpan = DateTime.Now.Subtract(startTime);

this.Message = string.Format("Finished processing in {0} ticks", this.ExecutionTimeSpan.Ticks);
return result;
}

public string Message
{
get;
set;
}

public Action<string> InvertMe
{
get;
set;
}

#endregion
}
For time being do not concentrate on the logic that is written inside the code, just concentrate on the attribute that I specified on the class. ExportAttribute is a special attribute that comes with MEF which is used to mark a class to be used as Plugin to another application that imports it. Or in other words, you can say, all the code that you want to expose to the application that hosts the plugins should be marked with Export attribute.

Hence you can say, the Types/ Methods/ Properties etc  once marked as Export will only be exposed to the Host which consumes the Plugins. I think you got the point clear now.  Isnt it.


You can also pass few other MetaData imformations on the Type using  [ExportMetadata("Foo", "Name")] attribute that can be associated with the type as well. Say for instance,


That means you can access the string value Message as KeyValuePair from the Imported Type of IProcessor. As the Value is actually an object type and also it supports Multiple, hence you can send as many information as you want with your type.

Once you are done lets add a new class to the Main Console Application to consume the exported IProcessor class. Lets see how the class looks like :

public class AssemblyProcessor
    {
        [Import(typeof(IProcessor)]
        public IProcessor CurrentProcessor { get; set; }

    }

Once you are done importing the IProcessor interface to the main application you need to determine how the plugin should be attached to the application. For simplicity lets Load the assembly from a certain location and pass it using Assembly.LoadFrom.

Assembly asm = Assembly.LoadFrom("SerialProcessor.dll");

var catalog = new AssemblyCatalog(asm);
            

var processor = new AssemblyProcessor();
var container = new CompositionContainer(catalog);

container.ComposeParts(processor);

if (processor.CurrentProcessor != null)
{
     IProcessor currentProcessor = processor.CurrentProcessor;
     currentProcessor.Process();

}

So here  few things that you need to understand. First of all, the First line in the code actually Loads the a Dll from file system to an assembly using Reflection Api. Once the assembly is successfully loaded in an assembly instance, we need to create an object of AssemblyCatalog. The assemblyCatalog class enumerates all the types, methods, fields that has Export Attribute in it and create a collection of ComposableParts. Oh wait, wait..  Lets define ComposablePart first.

ComposablePart

A ComposablePart is a composable unit in MEF. That means it holds all the information regarding either Export Unit (in our case IProcessor) or an Import Unit (nothing in our case, as our plugin does not include any Import Attribute). Hence each catalog exposes a property Parts which holds a collection of ExportDefination and ImportDefination where ExportDefination holds each Exports and ImportDefinations holds each Import.

If you loop through all the Parts of a catalog using the code below you can get all the information regarding the Exported type IProcessor.

foreach (var part in catalog.Parts)
{
    foreach (var exp in part.ExportDefinitions)
    {
        Console.WriteLine("Export Contract {0}", exp.ContractName);

        //Write MetaData 
        foreach (var metadata in exp.Metadata)
            Console.WriteLine("Metadata {0} for Part : {1} is {2}", metadata.Key, exp.ContractName, metadata.Value);
    }
}

The code will list all the Contracts defined within the assembly which is loaded in the catalog. The MetaData will list all the MetaData informations added to the Type using ExportMetaDataAttribute (which in our case is a message Serial Processor).

Once the Exports are loaded, its time to Map each Export defined in the dll (SerialProcessor.dll) with the corresponding Reference in the application which is defined within the class AssemblyProcessor. To do this we need a CompositionContainer.


var processor = new AssemblyProcessor();
var container = new CompositionContainer(catalog);
container.ComposeParts(processor);

if (processor.CurrentProcessor != null)
{
       IProcessor currentProcessor = processor.CurrentProcessor;
       currentProcessor.Process();

}

So here we first create an Instance of AssemblyProcessor that I have defined in the assembly and then create an instance of CompositionContainer and pass our Catalog into it.

The CompositionContainer gets Exports from the catalog and ultimately maps with Imports when ComposeParts extension method is called.

Hence when we call ComposeParts and pass a type (in our case AssemblyProcessor) it will find all the Import defined on the type and maps the Export with its respective Import.

What if I have more than one Type implements IProcessor with Export attribute ?

Interesting question. Yes in such a situation, you need to use ImportMany rather than Import. Lets see how to deal with such scenario.


If you have more than one Type that matches the criteria, you need to use ImportMany attribute to load it into an IEnumerable. Now the CurrentProcessor will hold a collection of types that implements IProcessor and also has an Export Attribute.

MEF is dynamically composed, How ?

Managed Extensibility Framework actually takes help of Reflection API to load Types dynamically during runtime. It exposes and maps Export with Import directly during runtime and invokes each method at runtime. So once the appropriate types are loaded you can use it normally in your code.

So our application is now ready. Lets try it now and it works.


So parallel will be invoked in First occation and then Serial. It also lists the Threads in Threadpool. You can find the complete application from here :



Please try the application.
I hope this gives you a brief idea on why we need MEF. Looking for more advanced thing? Stay tune, its coming very soon. Follow me to get updates.

Thanks for reading.
Happy Programming

No comments:

Post a Comment

Please make sure that the question you ask is somehow related to the post you choose. Otherwise you post your general question in Forum section.