In-Depth analysis of MEF Architecture

Let us take a deep dive into the MEF architecture. We can write sample applications by just going through the MEF knowledge base on the web. But to design an enterprise level of applications, it requires more in depth knowledge of what MEF is trying to solve, what MEF is, where it can be applied and how MEF is architected and designed to solve the existing issues in the software world.

Well, i did not invent MEF, so some of the information present in the blog is from the web. According to me, MEF is an Extensible Framework for composing applications from a set of loosely-coupled parts discovered and evolving at run-time. It is basically a composition framework.

What does MEF solve?

  • Managed Extensibility Framework is to solve the old problem of maintaining a piece of statically compiled software that continuously changes and evolves during its lifetime. MEF provides a solution for building reusable applications from reusable components which can be dynamically discovered at runtime by the application itself.
  • MEF offers a solution for extending the application architecture as it evolves.
  • With MEF, we can add additional metadata to the piece of code we write thus facilitating rich querying and filtering.
  • MEF is not a Dependency Injection container. DI is only a part of the solution that MEF tries to bring into the scene.

Why MEF?

From the efforts point of view, we approximately spend 20% to develop the original application and about 80% to maintain it during the years. If we could turn at least a part of that paining 80% to something more useful—for example inventing into creating better business value or user experience, etc.—both the customers and the company would be happier. That is where MEF comes into the picture from business perspective.

MEF Architecture

arch1

Fig: Different layers in the Managed Extensions Framework.

Container:

The namespaces of the different containers are as follows:

System.ComponentModel.Composition.Hosting.AggregateExportProvider
System.ComponentModel.Composition.Hosting.CatalogExportProvider
System.ComponentModel.Composition.Hosting.ComposablePartExportProvider
System.ComponentModel.Composition.Hosting.CompositionContainer

image     Fig: Class Hierarchy of the Containers.

The Container layer provides the public API’s to the users to use MEF. Let us examine the container layer.

CompositionContainer – The composition container, “CompositionContainer”, is present in the namespace System.ComponentModel.Composition.Hosting. The container contains a collection of parts that were either created by the container or added to it by the application. Parts contain a collection of exports (services it offers including itself) and imports (services it consumes) with imports being optional or required. One a Part has been added to the container, it can be composed. During composition, the container satisfies all of a Part’s imports based on available exports. If a required import cannot be satisfied, then composition will fail.

A CompositionContainer object serves two major purposes in an application. First, it keeps track of which parts are available for composition and what their dependencies are, and serves as the context for any given composition. Second, it provides the methods by which the application can initiate composition, get instances of composed parts, or fill the dependencies of a composable part. Parts can be made available to the container either directly or through the Catalog property. All the parts discoverable in this ComposablePartCatalog are available to the container to fulfill imports, along with any parts added directly.

ComposablePartExportProvider – This provider is responsible for retrieving exports from part catalogs. It contains a collection of parts. However the parts in it’s collection are created from the PartDefinitions it queries rather than being explicitly added to it. Also it contains a CompositionEngine for satisfying the imports on it’s parts.

CatalogExportProvider – Retrieves exports from a catalog.

AggregatingExportProvider – This provider is a composite of other providers that it contains, and is used for providing a topology of EPs. Whenever this provider is queried, it will query the providers within. The internal query behavior varies depending on the cardinality of the ImportDefinition that is passed in.  It queries it’s children. The container uses an AggregatingEP internally which contains a ComposablepartEP, a CatalogEP and/or a custom provider if one was passed in during its construction. AggregatingEPs can also be nested without a problem.

arch2.png

Fig: The chained set of ExportProvider instances in use by a container instance, so they can query each other for exports when satisfying dependencies.

Primitives:

Namespace: System.ComponentModel.Composition.Primitives

The Primitives is an abstraction layer. This layer provides a layer of indirection so MEF won’t be tight coupled with a single approach to part discovery, imports/exports definitions.  Actually, catalogs provide the available components to the container through this layer. The primitives provide APIs for describing, creating, and using components.

image

Fig: Overall view of the primitive classes.

The primitives represent component instances capable of being wired together, component definitions with rich meta data and common query interfaces for component catalogs. The role of the Primitives is to specify components that can be wired together to create useful software.

ComposablePart is an instance of live executing software component. The ComposablePart specifies its dependencies and capabilities through import and export respectively. The ComposablePart describes its own imports though ImportDefinition and exports through ExportDefinition

ExportDefinition is a structure comprising of a string-based ContractName and a dictionary of additional information called metadata. Each ExportDefnition attached to a composablepart describes the individual capability (service offered) of the part. Metadata is useful to perform some additional querying or filtering on the part.

ImportDefinition It describes the dependency of the part. IsRecomposable is useful to indicates whether the import definition can be satisfied multiple times. IsPrerequisite indicates whether the import definition must be satisfied  before a part can start producing exported objects.

ComposablePartDefinition describes the kinds of ComposablePart that can be created in a given system. It defines an abstract base class for composable part definitions, which describe and enable the creation of ComposablePart objects. CreatePart method creates a new instance of a part that the ComposablePartDefinition describes.

ComposablePartCatalog The ComposablePartDefinitions are grouped together into ComposablePartCatalogs. Using IQueryable<ComposablePartDefinition> Parts property, we can get the part definitions that are contained in the catalog.

 

Attributed Programming Model

Namespace: System.ComponentModel.Composition.Hosting

We can directly use the above mentioned Primitives in our applications to perform composition and other tasks but it is very laborious task. So there should be a way for the developers to interact with the primitives using some programming model. APM optimize the task of mapping a class or interface and members to a ComposablePartDefinition with exports and imports.

image  

Fig: Class Hierarchy of different types of Catalogs

The Attributed Programming Model is the implementation of the primitive layer.  Different catalogs can provide components that are defined using different programming models.  The default programming model for MEF is the attributed programming model which uses the Import and Export attributes.  The TypeCatalog, AssemblyCatalog, and DirectoryCatalog that ship with MEF use this programming model. The ability to use a different programming model provides a lot of flexibility.  Instead of using the Import and Export attributes, you could have your imports and exports defined based on a convention or an external configuration file.  You could also write a catalog where the components are defined using a dynamic language such as IronPython or IronRuby.  And you can use an AggregateCatalog to combine all of these catalogs and use them all together in the same container.

The primary interfaces to the Attributed Programming Model are:

  • TypeCatalog : Catalog containing discovered types
  • AssemblyCatalog: Catalog containing parts that are discovered in an assembly.
  • DirectoryCatalog: Catalog containing parts that are discovered in the directory.
  • AggregareCatalog: It holds the collection of Catalogs.

Well, that’s the high level view of the MEF internal structure. As mentioned earlier, the developer can use this framework using any programming model. In .Net 4.0, Microsoft provided the Attributed Programming Model to the developers to code the applications using MEF. Primitives are an abstraction layer, so tomorrow you can move from APM to any other model easily.

Concepts in MEF

MEF PPT 2010

A few essential concepts:

Part —A part provides services to other parts and consumes services provided by other parts. Parts in MEF can come from anywhere, from within the application or externally; from an MEF perspective, it makes no difference.

Export—An export is a service that a part provides. When a part provides an export, it is said that the part exports it. For example, a part may export a logger, or in the case of Visual Studio, an editor extension. Parts can provide multiple exports, though most parts provide a single export. Every Export has a contract, which determines what Imports it will be matched with. An export is a value (capabilities) that a part provides to other parts in the container

Import—An import is a service that a part consumes. Composable Parts declare imports through the attribute [Import]. 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. An Import is a requirement (Dependencies) that a part expresses to the container, to be filled from the available exports.

Contracts—A contract is an identifier for an export or an import. 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. An exporter specifies a string contract that it provides, and an importer specifies the contract that it needs. MEF derives contract names from the types that are being exported and imported, so in most cases you don’t have to think about it.

Catalog – A catalog is an object that makes available parts discovered from some source. MEF provides catalogs to discover parts from a provided type, an assembly, or a directory. So basically Catalog provides the parts to the application.

Composition—Parts are composed by MEF, which instantiates them and then matches up exporters to importers. The core of the MEF composition model is the composition container, which contains all the parts available and performs composition. (That is, the matching up of imports to exports.) The most common type of composition container is CompositionContainer. In order to discover the parts available to it, the composition containers makes use of a catalog.

Getting hands dirty

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 ex, 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 (eg. int, double, string, etc). Well, this is also an example of composition.

For ex, in C++, imagine we have the following two classes.

  1. class Class1
  2. {
  3.     ……….
  4.     ……….
  5. }

 

  1. class Class2
  2. {
  3.     public:
  4.             Class1 mobjclass1;        
  5.     ……….
  6.     ……….
  7. }

 

image   Fig: 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 Fig: 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.  Ones 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 ABC’s 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. With out 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  Fig: UML Class Diagram of the participating classes.

The source code can be downloaded from Google or Microsoft at  http://code.google.com/p/mef-dotnet/downloads/detail?name=MEFLab1.zip or from the link http://cid-38ecce05b21b8b44.office.live.com/self.aspx/MY%20Projects/MEFLab1.zip. 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 that 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:

  1. Add a New Project –> Visual C# –> Class Library.Enter Name as  MEFPart1. Click OK. Add the following code.
  2. Add a reference to the System.ComponentModel.Composition assembly.
  3. Add a reference to the ContractsLibrary.DLL assembly.
  4. Add the following using statement:
    • using System.ComponentModel.Composition; This allows us to specify the attributes for using MEF.
    • using ContractsLibrary;
  5. 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:

  1. Add a New Project –> Visual C# –> Console Application.Enter Name as  MainExe. Click OK. Add the following code.
  2. Add a reference to the System.ComponentModel.Composition assembly.
  3. Add a reference to the ContractsLibrary.DLL assembly.
  4. Add the following using statement:
    • using System.ComponentModel.Composition; This allows us to specify the attributes for using MEF.
    • using System.ComponentModel.Composition.Hosting;
    • using ContractsLibrary;
  5. Add the following code.
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

[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 a 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.

1) 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 

 

Importing Multiple Objects

In the previous blog, we discussed the basic MEF attributes. Let us get into advance topics in MEF step by step.

In the previous example, we wrote the code for the main exe, contract library and MEF Part1. Imagine we have another class derived from IGreeting and implements the SayHelloWorld() function. The class diagram and sequence diagram is shown below.

image

Fig: Class diagram of the participating classes.

image

Fig: Sequence Diagram for the above classes

In the previous blog, i posted the code of all the participating classes. Let us add the new UserGreetingClass to the solution.

Coding the MEFPart2 Library:

  1. Add a New Project –> Visual C# –> Class Library.Enter Name as  MEFPart2. Click OK. Add the following code.
  2. Add a reference to the System.ComponentModel.Composition assembly.
  3. Add a reference to the ContractsLibrary.DLL assembly.
  4. Add the following using statement:
    • using System.ComponentModel.Composition; This allows us to specify the attributes for using MEF.
    • using ContractsLibrary;
  5. Add the following code.
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel.Composition;
  4. using ContractsLibrary;
  5.  
  6. namespace MefLabPart2
  7. {
  8.     [Export(typeof(IGreeting))]
  9.     public class UserGreeting : IGreeting
  10.     {
  11.         public string SayHelloWorld()
  12.         {
  13.             return “Hello “ + Environment.UserDomainName;
  14.         }
  15.     }
  16. }

 

Compile the solution and copy the  MEFPart2.DLL to the MainExe\bin\Debug folder, where the MainExe.exe is located. Try running the application. The following error mesage comes up in the console.

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.

1) More than one export was found that matches the constraint ‘((exportDefinition.ContractName == “ContractsLibrary.IGreeting”) AndAlso (exportDefinition.Metadata.ContainsKey(“ExportTypeIdentity”) AndAlso “ContractsLibrary.IGreeting”.Equals
(exportDefinition.Metadata.get_Item(“ExportTypeIdentity”))))’.

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 issue here is that the catalog contains two exports and the import is importing only one export. So when the container tries to match the imports and exports, it fails with the above error.

image

Fig: Debug information of the catalog.

The Import attribute will only be successfully composed when it matches one and only one export. Other cases will produce a composition error. An ordinary ImportAttribute attribute is filled by one and only one ExportAttribute. If more than one is available, the composition engine produces an error. To import more than one export that matches the same contract, use the ImportMany attribute. Imports marked with this attribute are always optional. For example, composition will not fail if no matching exports are present. The following class imports any number of exports of type IGreeting.

  1. class Program
  2. {
  3.     [ImportMany]
  4.     public IEnumerable<IGreeting> Greetings { get; set; }

 

Following is the code of the MainExe project.

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 
    { 
      [ImportMany] 
      public IEnumerable<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); 
                CompositionBatch batch = new CompositionBatch(); 
                batch.AddPart(this); 
                container.Compose(batch); 
            } 
            catch (FileNotFoundException fnfex) 
            { 
                Console.WriteLine(fnfex.Message); 
            } 
            catch (CompositionException cex) 
            { 
                Console.WriteLine(cex.Message); 
            } 
        } 
  
        void Run() 
        { 
            foreach (var mgreeting in Greetings) 
            { 
                if (mgreeting != null) 
                { 
                    Console.WriteLine(mgreeting.SayHelloWorld()); 
                    Console.ReadKey(); 
                } 
            } 
        } 
    } 
} 

 

Source Code is available at :

Microsoft: http://cid-38ecce05b21b8b44.office.live.com/self.aspx/MY%20Projects/mef%202.zip

(OR) Google: MEF Lab 2 (http://code.google.com/p/mef-dotnet/downloads/detail?name=mef%202.zip)

Types of Imports

.Net 4.0 introduced many new programming concepts and MEF supports some of them. MEF supports several import types, including dynamic, lazy, prerequisite, and optional.

The attached source code contains some of the advance MEF programming concepts that I did not discuss in this blog. You can debug the source code step by step and explore the concepts.

Dynamic Imports:

.NET 4.0 introduces a new type, dynamic. The type is a static type, but an object of type dynamic bypasses static type checking. In most cases, it functions like it has type object. At compile time, an element that is typed as dynamic is assumed to support any operation. Therefore, you do not have to be concerned about whether the object gets its value from a COM API, from a dynamic language such as IronPython, from the HTML Document Object Model (DOM), from reflection, or from somewhere else in the program. However, if the code is not valid, errors are caught at run time.

In some cases, the importing class may want to match exports of any type that have a particular contract name. In this scenario, the class can declare a dynamic import. The following import matches any export with contract name “DemoOfDynamicImport”.

  1. [Import(“DemoOfDynamicImport”)]
  2. public dynamic somedynamicclassobject;

When the contract type is inferred from the dynamic keyword, it will match any contract type. In this case, an import should always specify a contract name to match on. (If no contract name is specified, the import will be considered to match no exports.) Both of the following exports would match the previous import. The importing class must be prepared to deal with an object of arbitrary type.

  1. [Export(“DemoOfDynamicImport”, typeof(IGreeting))]
  2. public class DemoOfDynamicImportClass
  3. {
  4.     public string SayHelloWorld()
  5.     {
  6.         return “Hello from DemoOfDynamicImport Class”;
  7.     }
  8. }
 
Lazy Imports:

In some cases, the importing class may require an indirect reference to the imported object, so that the object is not instantiated immediately. In this scenario, the class can declare a lazy import by using a contract type of Lazy<T>. The following importing property declares a lazy import. To explain multiple concepts, I created different objects using different techniques. Please use any one at your convenience.

   1:          [Import("IMyLazyClass",AllowDefault = true)]
   2:          public Lazy<mylazyclass> objlazyclass;
   3:   
   4:          [ImportMany]
   5:          public Lazy<IGreeting>[] Greetings1 { get; set; }
   6:   
   7:          [ImportMany]
   8:          public IEnumerable<Lazy<IGreeting>> Greetings2 { get; set; }

 

From the point of view of the composition engine, a contract type of Lazy<T> is considered identical to contract type of T. Therefore, the previous import would match the following export. The following Export matches the Import in line1 of the above code.

  1. [Export(“IMyLazyClass”)]
  2. class mylazyclass
  3. {
  4.     public string SayHelloWorld()
  5.     {
  6.         return “Hello from mylazyclass”;
  7.     }
  8. }

The following Export code matches the Import of line4 or line7 of the above Import code. The contract name and contract type can be specified in the Import attribute for a lazy import

  1. [Export(typeof(IGreeting))]
  2. public class SimpleGreetingClass : IGreeting
  3. {
  4.     public string SayHelloWorld()
  5.     {
  6.         return “Hello World !!!”;
  7.     }
  8.  
  9.     //Explicitly specifying a generic type.
  10.     [Export(typeof(Func<int, string>))]
  11.     public string DoSomething(int TheParam)
  12.     {
  13.         return “From Func: You Entered “ + TheParam.ToString();
  14.     }
  15. }

 

Optional Imports

The Import attribute specifies a requirement for the part to function. If an import cannot be fulfilled, the composition of that part will fail and the part will not be available. You can specify that an import is optional by using the AllowDefault property. In this case, the composition will succeed even if the import does not match any available exports, and the importing property will be set to the default for its property type (null for object properties, false for Booleans, or zero for numeric properties.) The following class uses an optional import.

[Import("IMyLazyClass",AllowDefault = true)]
public Lazy<mylazyclass> objlazyclass;

 

Metadata:

Exports can provide additional information about themselves known as metadata. Metadata can be used to convey properties of the exported object to the importing part. The importing part can use this data to decide which exports to use, or to gather information about an export without having to construct it. For this reason, an import must be lazy to use metadata. To use metadata, you typically declare an interface known as a metadata view, which declares what metadata will be available. The metadata view interface must have only properties, and those properties must have get accessors. To allow the accessing of metadata, MEF leverages a new API of the .NET Framework 4, System.Lazy<T>. It allows delaying the instantiation of an instance until the value property of the Lazy is accessed. MEF further extends Lazy<T> with Lazy<T,TMetadata> to allow accessing export metadata without instantiating the underlying export. TMetadata is a metadata view type. A metadata view is an interface that defines read-only properties that correspond to keys in the exported metadata. When the metadata property is accessed, MEF will dynamically implement TMetadata and will set the values based on the provided metadata from the export.

The following class is an example metadata. See line25.

  1. using System;
  2. using System.ComponentModel.Composition;
  3. using ContractsLibrary;
  4.  
  5. namespace MEFPart1
  6. {
  7.     [Export(typeof(IGreeting))]
  8.     public class SimpleGreetingClass : IGreeting
  9.     {
  10.         public string SayHelloWorld()
  11.         {
  12.             return “Hello World !!!”;
  13.         }
  14.  
  15.         //Explicitly specifying a generic type.
  16.         [Export(typeof(Func<int, string>))]
  17.         public string DoSomething(int TheParam)
  18.         {
  19.             return “From Func: You Entered “ + TheParam.ToString();
  20.         }
  21.     }
  22.  
  23.  
  24.     [Export(typeof(IGreeting))]
  25.     [ExportMetadata(“Name”, “Kishore”)]
  26.     public class SimpleMetaDataClass : IGreeting
  27.     {
  28.         public string SayHelloWorld()
  29.         {
  30.             return “Hello World from SimpleMetaDataClass. Checking the metadata name at runtime !!!”;
  31.         }
  32.     }
  33.  
  34. }
 
The following class is an example metadata. See line11
 
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.ComponentModel.Composition;
  6. using ContractsLibrary;
  7.  
  8. namespace MEFPart2
  9. {
  10.     [Export(typeof(IGreeting))]
  11.     [ExportMetadata(“Name”, “Babu”)]
  12.     public class ExtendedGreeting : IGreeting
  13.     {
  14.         public string SayHelloWorld()
  15.         {
  16.             return “Hello “ + Environment.UserDomainName + “\\” + Environment.UserName;
  17.         }
  18.     }
  19.  
  20.     //[PartNotDiscoverable]
  21.     // A part decorated with PartNotDiscoverabl attribute will not be included in any catalogs.
  22.     //So in the main exe, you will get runtime composition error
  23.     [Export(“DemoOfDynamicImport”, typeof(IGreeting))]
  24.     public class DemoOfDynamicImportClass
  25.     {
  26.         public string SayHelloWorld()
  27.         {
  28.             return “Hello from DemoOfDynamicImport Class”;
  29.         }
  30.     }
  31. }
 
An import with metadata is declared as a lazy import, with the metadata interface as the second type parameter to Lazy<T,T>. The following class imports the previous part with metadata.
   1:  [ImportMany]
   2:  public IEnumerable<Lazy<IGreeting, IGreetingMetadata>> greetingmetadataobjects { get; set; }

 

Once a collection of lazy exports with metadata has been imported, you can use LINQ (commented) to filter against the set as shown in the following code snippet.

  1. foreach (Lazy<IGreeting, IGreetingMetadata> greetingmeta in greetingmetadataobjects)
  2. {
  3.     if (greetingmeta.Metadata.Name == “Kishore”)
  4.     {
  5.         Console.WriteLine(greetingmeta.Value.SayHelloWorld());
  6.         //or we can create an object depending on the metadata value at runtime
  7.         IGreeting objgreeting = greetingmeta.Value;
  8.         Console.WriteLine(objgreeting.SayHelloWorld());
  9.     }
  10. }
  11.  
  12. ////The below commented code also performs the same task as the following code.
  13. ////Following is the code incase, you want to create an object of a class using LINQ
  14. //IEnumerable<IGreeting> objgreeting = greetingmetadataobjects.Where(v => v.Metadata.Name.Equals(“Kishore”)).Select(v => v.Value);
  15. //foreach (var greeting in objgreeting)
  16. //{
  17. //    if (greeting != null)
  18. //    {
  19. //        Console.WriteLine(greeting.SayHelloWorld());
  20. //    }
  21. //}
Source Code is available at :

Microsoft: http://cid-38ecce05b21b8b44.office.live.com/self.aspx/MY%20Projects/All%20MEF%20Concepts.zip

Have Fun !!!

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

 
Advertisements