Basics of Managed Extensibility Framework

Introduction

 
The objective of this article is to present a new feature of .NET 4.0 which is known as Managed Extensibility Framework.
 
Managed Extensibility Framework provides a way to create applications that can be extended dynamically and those extensions can be reused among multiple applications.
 
MEF allows application developers to discover and consume extensions with no configuration required. It also lets extension developers easily encapsulate code and avoid fragile hard dependencies.
 
Some details about the Managed Extensibility Framework:
  • When an application needs to consume external components, MEF provides a way to discover them implicitly.
     
  • There is a MEF component known as ComposablePart. It has two parts
     
    1. It defines all the capabilities the composable part offers
       
    2. Import: It defines the part's dependencies to other parts
       
  • These ComposableParts are discoverable at runtime & these are basically the extensions.
     
  • An extensible application using MEF declares import that can be filled by extension components & may also declare exports to expose services to extensions.
     
  • Each extension component declares an Export & may also declare Imports.
     
  • The core of the MEF composition model is the composition container which contains all the ComposableParts available & performs composition(connecting imports to exports).
     
  • The most common composition container is CompositionContainer.
I will explain the above-mentioned points by creating a sample application.
 
Step 1:
 
Create a new Console application and give it the name MyMEF.
 
Figure1.gif
 
Figure 1:
 
Step 2:
 
Add a new class to the project.
 
Figure2.gif
 
Figure 2:
 
Step 3:
 
Give it a name MEFMain.cs.
 
Figure3.gif
 
Figure 3:
 
This is actually our host application that uses MEF to extend itself.
 
Step 4:
 
Now add a reference of System.ComponentModel.Composition to the project.
 
Figure4.gif
 
Figure 4:
 
This namespace provides classes that constitute the core of the Managed Extensibility Framework.
 
Step 5:
 
Again add a new class to the project.
 
Figure5.gif
 
Figure 5:
 
Step 6:
 
Give it the name Extension1.cs.
 
Figure6.gif
 
Figure 6:
 
Step 7:
 
Modify the Extension1.cs in the following way.
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using System.ComponentModel.Composition;  
  6.   
  7. namespace MyMEF {  
  8.     class Extension1 {  
  9.         [Export]  
  10.         public string Message {  
  11.             get {  
  12.                 return " This is Extension 1";  
  13.             }  
  14.         }  
  15.     }  
Explanation of the code:
 
We can view this class as an extension. It has only one Export component i.e. a string read-only property.
 
I have defined the get accessor of the property which returns the value "This is Extension 1".
 
Step 8:
 
Modify the MEFMain class in the following way.
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using System.ComponentModel.Composition;  
  6. using System.ComponentModel.Composition.Hosting;  
  7. using System.Reflection;  
  8.   
  9. namespace MyMEF {  
  10.     class MEFMain {  
  11.   
  12.         [Import]  
  13.         public string Message { get;  
  14.             set; }  
  15.         public void HelloMEF() {  
  16.             CompositionContainer container = newCompositionContainer();  
  17.             CompositionBatch batch = newCompositionBatch();  
  18.             batch.AddPart(newExtension1());  
  19.             batch.AddPart(this);  
  20.             container.Compose(batch);  
  21.             Console.WriteLine(Message);  
  22.             Console.ReadLine();  
  23.         }  
  24.     }  
Explanation of the code:
 
I have added three namespaces here.
  1. System.ComponentModel.Composition  
  2. System.ComponentModel.Composition.Hosting [Used by the developer of the host application]  
  3. System.Reflection [To retrieve information of the assembly] 
Our host application has only one Import i.e. a string property Message which will be extended by the Export of Extension1. It has a method HelloMEF. In the method, I have created an instance of the CompositionContainer. Then I have added the composable part i.e. the instance of Extension1 & the application host itself to the instance of CompositionBatch. Then I have added the batch instance to the container. Now the container will perform the composition i.e. it will match the import to the correct export.
 
Step 9:
 
Modify the Program.cs in the following way.
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5.   
  6. namespace MyMEF {  
  7.     class Program {  
  8.         static void Main(string[] args) {  
  9.             MEFMainmefobj = newMEFMain();  
  10.             mefobj.HelloMEF();  
  11.         }  
  12.     }  
Explanation of the code:
 
I have created an instance of my host application MEFMain and called its HelloMEF function.
 
Step 10:
 
Now run the project.
 
Figure7.gif
 
Figure 7:
 
The container has done the composition. For this reason, the string property Message of our host application has got the value from the Export of Extension1.
 
Step 11:
 
Add one more class (Extension2.cs) to the project.
 
Figure8.gif
 
Figure 8:
 
Step 12:
 
Modify the Extension2.cs in the following way.
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using System.ComponentModel.Composition;  
  6.   
  7. namespace MyMEF {  
  8.     [Export]  
  9.     class Extension2 {  
  10.         public string GetMessage(string name) {  
  11.             return "Hello" + name;  
  12.         }  
  13.         public string GetMessage2(string name) {  
  14.             return "Hi" + name;  
  15.         }  
  16.     }  
Explanation of the code:
 
Here I have created one more extension and in this case, the Export is the class itself. It has two methods that take a string argument & also return a string.
 
Step 13:
 
Modify MEFMain in the following way.
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using System.ComponentModel.Composition;  
  6. using System.ComponentModel.Composition.Hosting;  
  7. using System.Reflection;  
  8.   
  9. namespace MyMEF {  
  10.     class MEFMain {  
  11.         [Import]  
  12.         public string Message { get;  
  13.             set; }  
  14.         [Import]  
  15.         public Extension2ext { get;  
  16.             set; }  
  17.         public void HelloMEF() {  
  18.             CompositionContainer container = newCompositionContainer();  
  19.             CompositionBatch batch = newCompositionBatch();  
  20.             batch.AddPart(newExtension1());  
  21.             batch.AddPart(newExtension2());  
  22.             batch.AddPart(this);  
  23.             container.Compose(batch);  
  24.             Console.WriteLine(Message);  
  25.             string msg = ext.GetMessage2("Urmi");  
  26.             Console.WriteLine(msg);  
  27.             Console.ReadLine();  
  28.         }  
  29.     }  
Explanation of the code:
 
In my host application, I have added one more Import that is a property of type Extension2 that will be extended by Extension2.
 
In the HelloMEF function, I have created an instance of Extension2 and added it to the container. I have called one method (Here GetMessage2) of the Extension2 using the extended property ext.
 
Step 14:
 
Now run the project.
 
Figure9.gif
 
Figure 9:
 
Step 15:
 
Modify MEFMain in the following way.
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using System.ComponentModel.Composition;  
  6. using System.ComponentModel.Composition.Hosting;  
  7. using System.Reflection;  
  8.   
  9. namespace MyMEF {  
  10.     class MEFMain {  
  11.         [Import]  
  12.         public string Message { get;  
  13.             set; }  
  14.         [Import]  
  15.         public Extension2ext { get;  
  16.             set; }  
  17.         public void HelloMEF() {  
  18.             CompositionContainer container = newCompositionContainer();  
  19.             CompositionBatch batch = newCompositionBatch();  
  20.             batch.AddPart(newExtension1());  
  21.             batch.AddPart(newExtension2());  
  22.             batch.AddPart(this);  
  23.             container.Compose(batch);  
  24.             Console.WriteLine(Message);  
  25.             string msg = ext.GetMessage("Urmi");  
  26.             Console.WriteLine(msg);  
  27.             Console.ReadLine();  
  28.         }  
  29.     }  
Here I have called the other method (Here GetMessage) of the Extension2 using the extended property ext.
 
Step 16:
 
Now run the project.
 
Figure10.gif
 
Figure 10:
 
Step 17:
 
Now modify MEFMain in the following way.
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using System.ComponentModel.Composition;  
  6. using System.ComponentModel.Composition.Hosting;  
  7. using System.Reflection;  
  8.   
  9. namespace MyMEF {  
  10.     class MEFMain {  
  11.         [Import]  
  12.         public string Message { get;  
  13.             set; }  
  14.         [Import]  
  15.         public Extension2ext { get;  
  16.             set; }  
  17.         public void HelloMEF() {  
  18.             var catalog = newAssemblyCatalog(System.Reflection.Assembly.GetExecutingAssembly());  
  19.             var container = newCompositionContainer(catalog);  
  20.             container.ComposeParts(this);  
  21.             //CompositionContainer container = new CompositionContainer();  
  22.             //CompositionBatch batch = new CompositionBatch();  
  23.             //batch.AddPart(new Extension1());  
  24.             //batch.AddPart(new Extension2());  
  25.             //batch.AddPart(this);  
  26.             //container.Compose(batch);  
  27.             Console.WriteLine(Message);  
  28.             string msg = ext.GetMessage("Urmi");  
  29.             Console.WriteLine(msg);  
  30.             Console.ReadLine();  
  31.         }  
  32.     }  
Explanation of the code:
 
Till now I have added the composable parts to the container explicitly. But by using the catalog, the container handles creating parts automatically rather than them having to be added explicitly. Here an instance of AssemblyCatalog is created with the executing assembly passed into the container's constructor. I am not adding the instance of Extension1 & Extension2 as it will be discovered in the catalog that was passed for the current assembly.
 
Step 18:
 
Now run the project.
 
Figure11.gif
 
Figure 11:
 
We get the same output.
 
Step 19:
 
Now modify Extension1.cs in the following way.
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using System.ComponentModel.Composition;  
  6.   
  7. namespace MyMEF {  
  8.     class Extension1 {  
  9.         [Export]  
  10.         public string Message {  
  11.             get {  
  12.                 return " This is Extension 1";  
  13.             }  
  14.         }  
  15.         [Import]  
  16.         public Extension2 ext1 { get;  
  17.             set; }  
  18.   
  19.         public void GetMsgExt() {  
  20.             string m = ext1.GetMessage("Uday");  
  21.             Console.WriteLine(m);  
  22.         }  
  23.     }  
Explanation of the code:
 
Now I am adding am Import to the Extension1 that will be extended by Export of Extension2. In MEF, the extensions can depend on each other.
 
Step 20:
 
Now modify MyMEF in the following way.
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using System.ComponentModel.Composition;  
  6. using System.ComponentModel.Composition.Hosting;  
  7. using System.Reflection;  
  8.   
  9. namespace MyMEF {  
  10.     class MEFMain {  
  11.         [Import]  
  12.         public string Message { get;  
  13.             set; }  
  14.         [Import]  
  15.         public Extension2ext { get;  
  16.             set; }  
  17.         public void HelloMEF() {  
  18.             //var catalog = new AssemblyCatalog(System.Reflection.Assembly.GetExecutingAssembly());  
  19.             //var container = new CompositionContainer(catalog);  
  20.             //container.ComposeParts(this);  
  21.             CompositionContainer container = newCompositionContainer();  
  22.             CompositionBatch batch = newCompositionBatch();  
  23.             Extension1extobj = newExtension1();  
  24.             batch.AddPart(extobj);  
  25.             batch.AddPart(newExtension2());  
  26.             batch.AddPart(this);  
  27.             container.Compose(batch);  
  28.             Console.WriteLine(Message);  
  29.             extobj.GetMsgExt();  
  30.             string msg = ext.GetMessage("Urmi");  
  31.             Console.WriteLine(msg);  
  32.             Console.ReadLine();  
  33.         }  
  34.     }  
Step 21:
 
Now run the project.
 
Figure12.gif
 
Figure 12: