Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

An Introduction to Managed Extensibility Framework (MEF) - Part I

0.00/5 (No votes)
27 Apr 2011 1  
This article will disscuss about MEF, how to start working in this, etc.

Table of Contents

  1. Introduction
  2. Background
  3. What is MEF?
  4. Assembly in which MEF resides
  5. MEF Constituents
  6. MEF related terminologies
  7. How MEF works
  8. A simple working example with only a single export part in current assembly
  9. A simple working example with multiple export part in current assembly
  10. A simple working example with multiple export part using Aggregate Catalog and Directory Catalog
  11. Advantages of MEF
  12. Conclusion
  13. History

Introduction

Though Managed Extensibility Framework (MEF) has been there a long time now, but still I think that there should be a simple article for a beginner to start with. It has now become an integral part of .NET Framework 4.0 and can be used in any kind of application. This is the first part of the series that I will be continuing on MEF. In the first part, we will learn about the basics and have a feel of MEF and at the end of this we should be able to figure out the benefit of MEF, how it helps in the development activities and in which situation we will adopt this. The article is written by keeping in mind that many developers are yet to start working on this and this article or the series will give them the feel of using it from scratch. So let us begin the journey.

Background

Working on MEF since the last few months was real fun. Henceforth, I thought of sharing the same with those who are about to start learning MEF from more of a step by step approach as well as for those who have done some MEF programming but did not get much exposure to it and wanted to learn more. We will start the journey from here with the very basic examples and will move ahead from time to time. This will be an n-th series of articles so in every part we will learn some new feature.

What is MEF?

Managed Extensibility Framework is a new framework from Microsoft Corporation to build Extensible applications. Its basic purpose is to plug-in components to an already running application. It is an integral part of .NET 4.0 Framework and it finds its applicability in any kind of .NET applications.

Assembly in Which MEF Resides

  • System.ComponentModel.Composition.dll

MEF Constituents

  • Import, Export and Compose

MEF Related Terminologies

Before going to talk more about MEF, let us see some of the basic terminologies that will help us in understanding MEF better.

  1. Part: A Part is an object (e.g. a class, a method or a property) that can be imported or exported to the application.
  2. Catalog: An object that helps in discovering the available composable parts from an assembly or a directory.
  3. Contract: The import and exported parts need to talk between themselves via some contract (e.g. an Interface or predefined data type like string)
  4. Import Attribute: It defines the need that a part has. It is applicable only for single Export Attribute.
  5. ImportMany Attribute: It is similar to Import Attribute but supports for multiple Export Attributes.
  6. Export Attribute: The import attribute creates the needs. The Export attribute fulfills that. It exposes those parts that will participate in the composition.
  7. Compose: In MEF jargon, Compose is that area where the Exported parts will be assembled with the imported ones.

At this point of time, the terminologies may not be clear to us. In order for the terminologies to be fathomable, let us first try to understand how MEF works.

How MEF Works

MEF works upon the principle of demand/need and supply of parts. The suppliable parts must be discovered and both the need and the suppliable parts must agree to the contract. Whenever there is a requirement of some parts to be plugged into the application, MEF will perform a dynamic discovery of those parts from some location (which we call as Catalog). The composition engine will assemble those parts as long as they satisfy the contract.

A Simple Working Example with Only a Single Export Part in Current Assembly

Let us create a simple calculator for the demonstration. Our sample application will have four basic arithmetic operations, viz. Add, Subtract, Multiply and Division. With that in mind, let us create our project structure as under:

1.jpg

The solution file CalculatorDemoUsingMEF has three projects:

  1. CompositionHelper
  2. CalculatorContract
  3. CalculatorUI

a) Description of CalculatorContract Project

Here, we basically define the contract or interface that the concrete components of Calculator say Add, Subtract, etc. must implement. The ICalculator interface is very simple as described below:

namespace CalculatorContract
{
    public interface ICalculator
    {
        int GetNumber(int num1, int num2);
    }
}

b) Description of CompositionHelper Project

In this project, we will create the various components needed for the Calculator like Addition, subtraction, etc. The Add.cs file is the component used for Addition operation.

Let us look into the Add.cs file:

using System.ComponentModel.Composition;
using CalculatorContract;

namespace CompositionHelper
{
    [Export(typeof(ICalculator))]
    public class Add:ICalculator
    {
        #region Interface members
        public int GetNumber(int num1, int num2)
        {
            return num1 + num2;
        }
        #endregion
    }
}

First of all, we need to add reference to System.ComponentModel.Composition assembly into the project.

2.jpg

Also a reference of the CalculatorContract .dll is added.

The class is decorated by the Export attribute which indicates that this class will be exported to the composition model and it respects only of the ICalculator contract/ interface type.

The CalcliComposition.cs file is the part where the Composition happens.

using System;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Reflection;
using CalculatorContract;

namespace CompositionHelper
{
    public class CalciCompositionHelper
    {
        [Import(typeof(ICalculator))]
        public ICalculator CalciPlugin { get; set; }

        /// <summary>
        /// Assembles the calculator components
        /// </summary>
        public void AssembleCalculatorComponents()
        {
            try
            {
                //Step 1: Initializes a new instance of the 
                //        System.ComponentModel.Composition.Hosting.AssemblyCatalog  
                //        class with the current executing assembly.
                var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());

                //Step 2: The assemblies obtained in step 1 are added to the 
                //CompositionContainer
                var container = new CompositionContainer(catalog);

                //Step 3: Composable parts are created here i.e. 
                //the Import and Export components 
                //        assembles here
                container.ComposeParts(this);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Sends the result back to the client
        /// </summary>
        /// <param name="num1"></param>
        /// <param name="num2"></param>
        /// <returns></returns>
        public int GetResult(int num1, int num2)
        {
           return CalciPlugin.GetNumber(num1, num2);
        }
    }
}

The AssembleCalculatorComponent function needs some attention. In this function, we are first looking for the catalogs from where the parts are coming. Once identified, the catalogs are held in a container and lastly the parts are composed.

c) Description of CalculatorUI Project

In this project, the UI is displayed. It adds a reference to the CalculatorComponentComposition dll.

Let us look into the Add button click event code:

objCompHelper = new CalciCompositionHelper();

//Assembles the calculator components that will participate in composition
objCompHelper.AssembleCalculatorComponents();

//Gets the result
var result = objCompHelper.GetResult(Convert.ToInt32(txtFirstNumber.Text), 
		Convert.ToInt32(txtSecondNumber.Text));

//Display the result
txtResult.Text = result.ToString();

First, we are assembling the calculator components that will participate in the composition. Then calling the Addition component for the needed operation.

The final output is as shown below:

3.jpg

A Simple Working Example with Multiple Export Part in Current Assembly

Well by this time, we have learnt something about MEF, how it works and also we have successfully added one component in our application. Now let us enhance our application so that it can handle all the other components like Subtraction, Multiplication and Division.

So let us add the various component parts in the application. The project structure will now become:

4.jpg

As can be figured out, there is not much change expected, we have added three new parts which are highlighted.

Let us now visit the Add.cs file again:

using System.ComponentModel.Composition;
using CalculatorContract;

namespace CompositionHelper
{
    [Export(typeof(ICalculator))]
    [ExportMetadata("CalciMetaData", "Add")]
    public class Add:ICalculator
    {
        #region Interface members
        public int GetNumber(int num1, int num2)
        {
            return num1 + num2;
        }
        #endregion
    }
}

We have found that there is a new attribute call ExportMetadata. This new attribute will help us to determine at runtime which implementation to use. It is a form of name value pair. The first parameter of this attribute is the name of the metadata. It's type is string. The next one which is an object holds the value of the metadata. It is by which we will decide which operation will be called. A similar implementation has been followed for the rest of the Exportable parts.

Next, let us have a look at the CalciCompositionHelper.cs file:

using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Reflection;
using CalculatorContract;

namespace CompositionHelper
{
    public class CalciCompositionHelper
    {
        [ImportMany]
        public System.Lazy<ICalculator, 
	IDictionary<string, object>>[] CalciPlugins { get; set; }

        /// <summary>
        /// Assembles the calculator components
        /// </summary>
        public void AssembleCalculatorComponents()
        {
            try
            {
                //Step 1: Initializes a new instance of the 
                //        System.ComponentModel.Composition.Hosting.AssemblyCatalog  
                //        class with the current executing assembly.
                var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());

                //Step 2: The assemblies obtained in step 1 are added to the 
                //CompositionContainer
                var container = new CompositionContainer(catalog);

                //Step 3: Composable parts are created here i.e. 
                //the Import and Export components 
                //        assemble here
                container.ComposeParts(this);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Sends the result back to the client
        /// </summary>
        /// <param name="num1"></param>
        /// <param name="num2"></param>
        /// <returns></returns>
        public int GetResult(int num1, int num2, string operationType)
        {
            int result = 0;
            foreach (var CalciPlugin in CalciPlugins)
            {
                if ((string)CalciPlugin.Metadata["CalciMetaData"] == operationType)
                {
                    result = CalciPlugin.Value.GetNumber(num1, num2);
                    break;
                }                               
            }
            return result;           
        }
    }
}

The first change that we observe is that now we have ImportMany attribute instead of Import attribute. The Import attribute is filled always by a single Export attribute while ImportMany attribute can be filled by any number of Export attribute.

When we run the application, we can figure out the various parts that participate in composition.

5.jpg

One more noticeable point is the Lazy<T> class basically defers the creation of large objects till the time we need that. In the sample code, the second parameter is the metadata that will be handled by MEF at runtime.

Correspondingly, the GetResult function has been changed a little bit. It now accepts a third parameter which specifies the operation type and based on that, the Exported parts will be invoked.

Finally, on the Concrete operations click event, we are passing the Operation types as:

private void btnAdd_Click(object sender, RoutedEventArgs e)
{
            DoCalciOperation("Add");
}

Once we run the application, the output will be as under (output shown only for multiplication operation):

6.jpg

Hope this is equally helpful as the previous one. In the next part, we will explore some new catalog types and more features of MEF.

A Simple Working Example with Multiple Export Part using Aggregate Catalog and Directory Catalog

Until now, we have seen that our Composable parts were residing in the current assembly. But what if they reside in different assemblies or in different location?

Well, we have a solution for this too. We will see how Aggregate and Directory Catalog help us in such situations.

Let us see the project structure now.

7.jpg

We can figure out that one new project has been added to the solution file, i.e., Export Components. In that, we have a folder called Components where our export parts in the form of assemblies are stored (.dll files).

We have added only two DLLs there, viz., Add and Subtraction and removed the Add.cs and Subtract.cs files from CompositionHelper project.

Now let us visit the AssembleCalculatorComponent function again.

public void AssembleCalculatorComponents()
        {
            try
            {
                //Creating an instance of aggregate catalog. It aggregates other catalogs
                var aggregateCatalog = new AggregateCatalog();
                
                //Build the directory path where the parts will be available
                var directoryPath =
                    string.Concat(Path.GetDirectoryName
			(Assembly.GetExecutingAssembly().Location)
                          	.Split('\\').Reverse().Skip(3).Reverse().Aggregate
			((a, b) => a + "\\" + b)
                                , "\\", "ExportComponents\\Components");

                //Load parts from the available DLLs in the specified path 
                //using the directory catalog
                var directoryCatalog = new DirectoryCatalog(directoryPath, "*.dll");

                //Load parts from the current assembly if available
                var asmCatalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());

                //Add to the aggregate catalog
                aggregateCatalog.Catalogs.Add(directoryCatalog);
                aggregateCatalog.Catalogs.Add(asmCatalog);

                //Crete the composition container
                var container = new CompositionContainer(aggregateCatalog);

                // Composable parts are created here i.e. 
                // the Import and Export components assembles here
                container.ComposeParts(this);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

As can be seen, initially we have created an AggregateCatalog that will aggregate other catalogs. Then we are picking up the Exportable Components from the Components directory.

If we run the application, we can find that the Directory Catalog has two parts (Add and Subtract).

8.jpg

And the AssemblyCatalog has two parts (Divide and Multiply):

9.jpg

After the parts have been discovered, they are then added to the AggragateCatalog which is being added to the CompositionContainer. And finally, the parts gets composed.

Running the application brings out the desired output (output shown only for Addition operation).

10.jpg

Advantages

MEF has many advantages as we have seen by this time. However, some of the common advantages are listed below:

  1. MEF breaks the tightly coupled dependencies across the application but respects the type checking of the loosely coupled parts.
  2. Applications can be extended.
  3. Components can be added at runtime.
  4. Dynamic discovery of the components.
  5. Great piece of reusability.

Conclusion

So in this first part of the MEF series, we learnt about the basics of MEF, its applicability, importance of Export, Import, ImportMany attributes, Assembly and Directory catalogs, dynamic component discovery, etc. But there is a lot more stuff to learn about MEF. In the next part or subsequent parts, we will look into some attributes, catalogs, more practical examples with different kind of applications using MEF. Thanks for reading.

History

  • 28th April, 2011: Initial post

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here