- Download source code from here
In the previous blog post, we discussed the MEF Architecture in detail. Let us start writing some code using MEF in C#.NET. First things first, you need the current version of .NET Framework 4.0 and Visual Studio 2010 to write code.
Note: For the UML diagrams, I am using the diagrams provided in the Architecture menu of Visual Studio 2010 Ultimate Edition. VS 2010 is not as powerful as Rational Rose or Enterprise Architect but you can design the basic UML diagrams.
Composition Basics
Compose means "To put things together to form a whole." Composition is used for objects that have a has-a relationship to each other. For example, a& car has-an engine, and has-a transmission, etc. A personal computer has-a CPU, has-a motherboard, etc.& we have designed classes that are composed of member variables which are built-in data types (e.g. int
, double
, string
, etc.). Well, this is also an example of composition.
For example, in C++, imagine we have the following two classes:
class Class1
{
……….
……….
}
&
class Class2
{
public:
Class1 mobjclass1;
……….
……….
}
Figure: Composition relationship between class1 and class2. Class2 has a object of Class1.
The black diamond represents composition. It is placed on the Class2
class because it is the Class2
that is composed of a Class1
. The arrowhead on the other end of the relationship denotes that the relationship is navigable in only one direction. That is, Class1
does not know about Class2
. Composition is great but the disadvantage is that we introduce tight coupling (line 4 in Class2
) resulting in higher code maintenance cost and difficult to extend the class for additional functionality.
Prologue to MEF
Ok, let’s start with the most simple example: Hello World
!!!
Figure: Relationship diagram between the two classes
Let us assume a class SimpleGreetingClass
, which has a member function public string SayHelloWorld()
, which returns "Hello World
!!!" string
. Let us assume another class Program
, which has an attribute SimpleGreetingClass mobjsimplegreetingclass
. Once& we create the object mobjsimplegreetingclass
, we can call the member function SayHelloWorld()
using this object. Following is the code for the same.
namespace MEFLab1
{
public class SimpleGreetingClass
{
public string SayHelloWorld()
{
return "Hello World !!!";
}
}
class Program
{
static void Main(string[] args)
{
Program program = new Program();
program.Run();
}
void Run()
{
SimpleGreetingClass mobjsimplegreetingclass = new SimpleGreetingClass();
Console.WriteLine(mobjsimplegreetingclass.SayHelloWorld());
Console.ReadKey();
}
}
}
As we can see, there is a tight coupling between the SimpleGreetingClass
and Program
class. Also, the Program
class must be aware of SimpleGreetingClass
during the compilation time. Well, there are many disadvantages in this kind of design.
One of the USP (Unique Selling Point) of MEF is to solve this kind of tight coupling problem. Let us dive deep into MEF Programming and see how MEF solves this issue and other problems in the software world.
Enter the World of MEF
The ABCs of MEF are:
- Export it.
- Import it.
- Compose it.
Well, I am going to use some design pattern techniques along with MEF as I always promote the best practices of software design in my architecture. Please observe the changes in the class diagram from the previous example. I used the IGreeting interface
as it is a good practice to expose your contracts through interface
(Facade Design Pattern). Our SimpleGreetingClass
inherits IGreeting interface
. This is the technique for Decoupling Implementation using an Interface. Without this, a tightly coupled relationship will be formed between participating classes. This limits extensibility options as well as testability of the class itself. MEF allows imports to be decoupled from the exporter’s implementation by using an interface
as the contract.
Figure: UML Class Diagram of the participating classes.
The source code can be downloaded from Google or Microsoft here& or from this link. Below is the detailed explanation of each of the projects present in solution1
in the attached zip file.
Coding the Interface Library
- Create New Solution using VS 2010.
- Add a New Project –> Visual C# –> Class
Library.Enter
Name as ContractsLibrary
. Click OK. Add the following code. - Here we are just creating an
interface IGreeting
so that the host application can accept components through the IGreeting interface
.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ContractsLibrary
{
public interface IGreeting
{
string SayHelloWorld();
}
}
Coding the MEFPart1 Library
- Add a New Project –> Visual C# –> Class
Library.Enter
Name as MEFPart1
. Click OK. Add the following code. - Add a reference to the
System.ComponentModel.Composition
assembly. - Add a reference to the
ContractsLibrary.DLL
assembly. - Add the following
using
statement:
using System.ComponentModel.Composition;
using ContractsLibrary;
Add the following code:
using System.ComponentModel.Composition;
using ContractsLibrary;
namespace MEFPart1
{
[Export(typeof(IGreeting))]
public class SimpleGreetingClass : IGreeting
{
public string SayHelloWorld()
{
return "Hello World !!!";
}
}
}
Here, MEF makes this piece of code available to other components as it is specified(decorated) with attribute [Export]
. We know that the contract consists of a string
, called the contract name, and the type of the exported or imported object, called the contract type. Only if both the contract name and contract type match is an export considered to fulfill a particular import. Let us examine the line [Export(typeof(IGreeting))] ;
the contract type is IGreeting
because it is specified as a parameter of the Export
attribute. The exported type must be either the same as the contract type, derive from the contract type, or implement the contract type if it is an interface
. In this export, the actual type SimpleGreetingClass
implements the interface IGreeting
.
So in this case, the contract type will be IGreeting
, and the contract name will be a unique string
created from the contract type. In other words, the contract name will match only exports whose names are also inferred from the type IGreeting
.
Coding the MainExe
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using ContractsLibrary;
- Add a New Project –> Visual C# –> Console
Application.Enter
Name as MainExe
. Click OK. Add the following code. - Add a reference to the
System.ComponentModel.Composition
assembly. - Add a reference to the ContractsLibrary.DLL assembly.
- Add the following
using
statement: - Add the following code:
using System;
using System.IO;
using System.Reflection;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using ContractsLibrary;
using System.Collections.Generic;
namespace meflab1
{
class Program
{
[Import(typeof(IGreeting))]
public IGreeting Greetings { get; set; }
static void Main(string[] args)
{
Program program = new Program();
program.Run();
}
public Program()
{
try
{
AggregateCatalog aggregatecatalogue = new AggregateCatalog();
aggregatecatalogue.Catalogs.Add(new DirectoryCatalog(
AppDomain.CurrentDomain.BaseDirectory));
CompositionContainer container = new CompositionContainer(
aggregatecatalogue);
container.ComposeParts(this);
}
catch (FileNotFoundException fnfex)
{
Console.WriteLine(fnfex.Message);
}
catch (CompositionException cex)
{
Console.WriteLine(cex.Message);
}
}
void Run()
{
if (Greetings != null)
{
Console.WriteLine(Greetings.SayHelloWorld());
Console.ReadKey();
}
}
}
}
Observe the code:
[Import]
public IGreeting Greetings { get; set; }
The [Import]
attribute declares something to be an import; that is, it will be filled by the composition engine when the object is composed. Every import has a contract, which determines what exports it will be matched with. The contract can be an explicitly specified string
, or it can be automatically generated by MEF from a given type, in this case the interface IGreeting
. Any export declared with a matching contract will fulfill this import. In this import, the Import
attribute has neither a contract type nor a contract name parameter attached. Therefore, both will be inferred from the decorated property. In this case, the contract type will be IGreeting
, and the contract name will be a unique string
created from the contract type. MEF will automatically assume the contract to be based on the type of the import unless you specify it explicitly. We can also code it as [Import(typeof(IGreeting))]
.
MEF Composition Explained
With MEFPart1
and ContractsLibrary
in place, you can now start composition. MEF parts don’t automatically get discovered or created. Instead, you need to write some bootstrapping code that will enact composition. A common place to do this is in our application’s entry point, which in this case is the Program
class.
To bootstrap MEF involves a few steps:
- Add imports of the contracts you need the container to create, which I& explained in the above section.
- Create a catalog that MEF uses to discover parts.
- Create a container that composes instances of parts.
- Compose by calling the
Composeparts
method on the container and passing in the instance that has the imports.
As you can see here, I added an IGreeting
import on the Program
class. Then I created a DirectoryCatalog
pointing to the base folder containing the MainExe
. Then created a container that uses the catalog. Finally, I called Composeparts
, which caused an App instance to be composed and the IGreeting
import to be satisfied.
During composition, the container will create the IGreeting
and satisfy its SimpleGreetingClass
import. This will result in SimpleGreetingClass
being created. Finally, the Application
class will have its IGreeting
import satisfied. In this way, MEF has assembled the entire object graph based on declarative information, rather than manually requiring imperative code to do the assembly. As I& said before, container acts as a match maker.
Epilogue
Build the solution and run the Mainexe
. The console window pops up and displays the following error information.
The composition remains unchanged. The changes were rejected because of the following error(s): The composition produced a single composition error. The root cause is provided below. Review the CompositionException.Errors
property for more detailed information.
No valid exports were found that match the constraint ‘((exportDefinition.ContractName == "ContractsLibrary.IGreeting") AndAlso (exportDefinition.Metadata.ContainsKey("ExportTypeIdentity") AndAlso "ContractsLibrary.IGreeting".Equals(exportDefinition.Metadata.get_Item("ExportTypeIdentity"))))’
, invalid exports may have been rejected.
Resulting in: Cannot set import ‘meflab1.Program.Greetings (ContractName="ContractsLibrary.IGreeting")
’ on part ‘meflab1.Program’. Element: meflab1.Program.Greetings (ContractName="ContractsLibrary.IGreeting")
-
-> meflab1.Program
Press any key to continue . . .
The reason is that the [Import]
in the MainExe
could not be satisfied as there the catalog provided to the container does not have any parts.
Copy the MEFPart1.DLL to the folder containing the MainExe
executable. Then run the MainExe
and you can see Hello World
!!! on the console window. This is because we used directory catalog and it detects all the parts present in the directory and provides it to the container to satisfy the exports and import definitions.
I will be explaining other concepts of MEF in my upcoming blog. Below is the Class Diagram and Sequence Diagram for the code that I& am writing.
“A leader takes people where they want to go. A great leader takes people where they don’t necessarily want to go, but ought to be.”