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 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
- It defines all the capabilities the composable part offers
- 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 a name MyMEF.
Figure 1:
Step 2:
Add a new class to the project.
Figure 2:
Step 3:
Give it a name MEFMain.cs.
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.
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.
Figure 5:
Step 6:
Give it a name Extension1.cs.
Figure 6:
Step 7:
Modify the Extension1.cs as the following way.
using
System;
usingSystem.Collections.Generic;
usingSystem.Linq;
usingSystem.Text;
using
System.ComponentModel.Composition;
namespaceMyMEF
{
classExtension1
{
[Export]
publicstring
Message
{
get
{
return"
This is Extension 1";
}
}
}
}
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 as the following way.
using
System;
usingSystem.Collections.Generic;
usingSystem.Linq;
usingSystem.Text;
using
System.ComponentModel.Composition;
using
System.ComponentModel.Composition.Hosting;
using
System.Reflection;
namespaceMyMEF
{
classMEFMain
{
[Import]
publicstring
Message { get; set;
}
publicvoid
HelloMEF()
{
CompositionContainer
container = newCompositionContainer();
CompositionBatch
batch = newCompositionBatch();
batch.AddPart(newExtension1());
batch.AddPart(this);
container.Compose(batch);
Console.WriteLine(Message);
Console.ReadLine();
}
}
}
Explanation of the code:
I have added three namespaces here.
System.ComponentModel.Composition
System.ComponentModel.Composition.Hosting [Used by the developer of the
host application]
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 as the following way.
using
System;
usingSystem.Collections.Generic;
usingSystem.Linq;
usingSystem.Text;
namespaceMyMEF
{
classProgram
{
staticvoid Main(string[]
args)
{
MEFMainmefobj
= newMEFMain();
mefobj.HelloMEF();
}
}
}
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.
Figure 7:
The container has done the composition. For the 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.
Figure 8:
Step 12:
Modify the Extension2.cs as the following way.
using
System;
usingSystem.Collections.Generic;
usingSystem.Linq;
usingSystem.Text;
using
System.ComponentModel.Composition;
namespaceMyMEF
{
[Export]
classExtension2
{
publicstring
GetMessage(string name)
{
return"Hello"
+ name;
}
publicstring
GetMessage2(string name)
{
return"Hi"
+ name;
}
}
}
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 as the following way.
using
System;
usingSystem.Collections.Generic;
usingSystem.Linq;
usingSystem.Text;
using
System.ComponentModel.Composition;
using
System.ComponentModel.Composition.Hosting;
using
System.Reflection;
namespaceMyMEF
{
classMEFMain
{
[Import]
publicstring
Message { get; set;
}
[Import]
publicExtension2ext
{ get; set; }
publicvoid
HelloMEF()
{
CompositionContainer
container = newCompositionContainer();
CompositionBatch
batch = newCompositionBatch();
batch.AddPart(newExtension1());
batch.AddPart(newExtension2());
batch.AddPart(this);
container.Compose(batch);
Console.WriteLine(Message);
stringmsg
= ext.GetMessage2("Urmi");
Console.WriteLine(msg);
Console.ReadLine();
}
}
}
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.
Figure 9:
Step 15:
Modify MEFMain as the following way.
using
System;
usingSystem.Collections.Generic;
usingSystem.Linq;
usingSystem.Text;
using
System.ComponentModel.Composition;
using
System.ComponentModel.Composition.Hosting;
using
System.Reflection;
namespaceMyMEF
{
classMEFMain
{
[Import]
publicstring
Message { get; set;
}
[Import]
publicExtension2ext
{ get; set; }
publicvoid
HelloMEF()
{
CompositionContainer
container = newCompositionContainer();
CompositionBatch
batch = newCompositionBatch();
batch.AddPart(newExtension1());
batch.AddPart(newExtension2());
batch.AddPart(this);
container.Compose(batch);
Console.WriteLine(Message);
stringmsg
= ext.GetMessage("Urmi");
Console.WriteLine(msg);
Console.ReadLine();
}
}
}
Here I have called the other method (Here GetMessage) of the Extension2 using
the extended property ext.
Step 16:
Now run the project.
Figure 10:
Step 17:
Now modify MEFMain as the following way.
using
System;
usingSystem.Collections.Generic;
usingSystem.Linq;
usingSystem.Text;
using
System.ComponentModel.Composition;
using
System.ComponentModel.Composition.Hosting;
using
System.Reflection;
namespaceMyMEF
{
classMEFMain
{
[Import]
publicstring
Message { get; set;
}
[Import]
publicExtension2ext
{ get; set; }
publicvoid
HelloMEF()
{
var
catalog = newAssemblyCatalog(System.Reflection.Assembly.GetExecutingAssembly());
var
container = newCompositionContainer(catalog);
container.ComposeParts(this);
//CompositionContainer container = new CompositionContainer();
//CompositionBatch batch = new CompositionBatch();
//batch.AddPart(new Extension1());
//batch.AddPart(new Extension2());
//batch.AddPart(this);
//container.Compose(batch);
Console.WriteLine(Message);
stringmsg
= ext.GetMessage("Urmi");
Console.WriteLine(msg);
Console.ReadLine();
}
}
}
Explanation of the code:
Till now I have added the composable parts to the container explicitly. But by
using 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.
Figure 11:
We get the same output.
Step 19:
Now modify Extension1.cs as the following way.
using
System;
usingSystem.Collections.Generic;
usingSystem.Linq;
usingSystem.Text;
using
System.ComponentModel.Composition;
namespaceMyMEF
{
classExtension1
{
[Export]
publicstring
Message
{
get
{
return"
This is Extension 1";
}
}
[Import]
publicExtension2
ext1 { get; set;
}
publicvoidGetMsgExt()
{
string
m = ext1.GetMessage("Uday");
Console.WriteLine(m);
}
}
}
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 as the following way.
using
System;
usingSystem.Collections.Generic;
usingSystem.Linq;
usingSystem.Text;
using
System.ComponentModel.Composition;
using
System.ComponentModel.Composition.Hosting;
using
System.Reflection;
namespaceMyMEF
{
classMEFMain
{
[Import]
publicstring
Message { get; set;
}
[Import]
publicExtension2ext
{ get; set; }
publicvoid
HelloMEF()
{
//var catalog =
new AssemblyCatalog(System.Reflection.Assembly.GetExecutingAssembly());
//var container = new CompositionContainer(catalog);
//container.ComposeParts(this);
CompositionContainer
container = newCompositionContainer();
CompositionBatch
batch = newCompositionBatch();
Extension1extobj
= newExtension1();
batch.AddPart(extobj);
batch.AddPart(newExtension2());
batch.AddPart(this);
container.Compose(batch);
Console.WriteLine(Message);
extobj.GetMsgExt();
stringmsg =
ext.GetMessage("Urmi");
Console.WriteLine(msg);
Console.ReadLine();
}
}
}
Step 21:
Now run the project.
Figure 12: