Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Introduction to MEF Programming

3.74/5 (5 votes)
25 Aug 2010CPOL8 min read 44.1K  
Introduction to MEF Programming
  • 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:

C++
class Class1
{ 
    ……….
    ……….
}

&

C++
class Class2
{
    public:
            Class1 mobjclass1;         
    ………. 
    ………. 
}

image

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!!!

image

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.

C#
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.

image

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

  1. Create New Solution using VS 2010.
  2. Add a New Project –> Visual C# –> Class Library.Enter Name as ContractsLibrary. Click OK. Add the following code.
  3. Here we are just creating an interface IGreeting so that the host application can accept components through the IGreeting interface.
C#
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:
C#
// This allows us to specify the attributes for using MEF. 
using System.ComponentModel.Composition;

using ContractsLibrary;

Add the following code:

C#
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

C#
// This allows us to specify the attributes for using MEF. 
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:
C#
using System;
using System.IO;
using System.Reflection;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
//using System.ComponentModel.Composition.Primitives;
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 AssemblyCatalog(
                //    Assembly.GetExecutingAssembly()));
                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:

C#
[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.

image

image

“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.”

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)