Click here to Skip to main content
15,867,308 members
Articles / Programming Languages / C# 4.0

MEF 2 Preview Beginners Guide

Rate me:
Please Sign up or sign in to vote.
4.84/5 (13 votes)
18 Apr 2012CPOL17 min read 76.7K   1.7K   38   9
A short guide to what to look forward to in the next release of MEF.

Introduction

This is the year all .NET developers will be licking there lips at the excitement and anticipation around yet another version release of our beloved framework of choice. So in the spirit of all things new and exciting. Today i'll be discussing one of my favourites MEF. We all know about MEF's sheer ability to bring about simplicity in implementation, in a development environment defined by complexities, on a day to day basis. Firstly I will be discussing the key features that have been added to MEF2 so far, that I think will make a massive impact upon the release of .NET 4.5. However please note this is more of a sneak peek and not all of what I show you will be in the final version. At time of writing this article the current MEF 2 version is Preview. The new features that will be discussed include The ExportFactory<T> and the new registration API called the RegistrationBuilder to name a few. So let's dive in. 

Background 

This is a beginners' guide to quickly get you familiar to some of the key changes that have been implemented in MEF 2. So you can start using the preview version straight away with no additional research and understanding.

Setting it all up

Firstly for those of you who have already used the first iteration of MEF, you will have noticed a few distinct differences between the first release and the new upcoming release. One of the subtle changes is all the common export catalogs; now contain an additional constructor overload. This parameter argument is for a new class called the RegistrationBuilder. This class handles the registration of all your exports and part creation through the RegistrationBuilders rich API. So you no longer need to worry about placing attributes on all your dependencies. This was a great move by the team who developed MEF, because MEF’s main role is to provide an easy implementation of dependency injected types for loosely coupled architecture and extensible projects. Any developer extending the functionality of a project doesn’t have to now worry about setting up attributes on the objects he or she wishes to inject. Let’s jump into an example setup below; As you can see in the code snippet below MEF is more flexible with its object lifetime management, as well as the other new features I mentioned above. Now that you are completely bored with me rambling on let me show you how the container and a simple export is set up.

C#
var convention = new RegistrationBuilder();
//export a type implementing an interface.
convention.ForTypesDerivedFrom<igreetings>().Export<greetings>();
As you can see we instantiate the new RegistrationBuilder class and then use its Fluent API to map the dependency. In this simple case, a concrete type that implements an interface is being exported. For those of you who haven’t used MEF 1 the equivalent would be the following in MEF 1.
C#
[InheritedExport] //MEF 1
public interface IGreetings
{
    void Greet(string name);
}
public class Greetings : IGreetings

Now registering the container would be as follows.

C#
var assemblyCatalog = new AssemblyCatalog(Assembly.GetExecutingAssembly(), registeredExports);
var catalog = new AggregateCatalog(assemblyCatalog);
container = new CompositionContainer(catalog, 
                CompositionOptions.DisableSilentRejection | CompositionOptions.IsThreadSafe);
container.ComposeParts(this);

Note that we now pass in the RegistrationBuilder’s instance variable registeredExports to our assemblyCatalog’s constructor. And then the rest of the process is virtually the same as MEF 1. There is one more line that needs to be discussed though. The CompositionContainer’s constructor contains an overload for an enumeration of CompositionOptions when composing parts.

C#
container = new CompositionContainer(catalog, CompositionOptions.DisableSilentRejection | CompositionOptions.IsThreadSafe);

Available Options are:

CompositionOption Description 
Default Is the default implementation and settings. 
DisableSilentRejection  Shows more detailed exceptions when an import/export fails. 
IsThreadSafe Composes the exports in a thread safe manner. 
ExportCompositionService  Sets the Export composition service. 

The reason for these options is to better manage the composition of the catalogs parts as well as provide more informative exceptions to better diagnose failed exports. This by the way was a nightmare with MEF 1. As you may have already noticed MEF 2 is far more flexible in the way exports can be set and imported. So let’s focus our discussion on that now and dissect the RegistrationBuilders API to further understand how exports are registered and imported into types and finally consumed by the container.

Registering Types the Flexible Way

MEF has three ways of going about registering a particular type and exporting it.

  1. ForType() & ForType<>(): Allows you to specify any type that you wish to export either by way of it’s known type at compile time using the generic method. Or by way of passing in a type of an object that is only known at runtime.

    C#
    convention.ForType<test>()
  2. ForTypesMatching() & ForTypesMatching<>(): Allows you to export types based on a predicate filter.
    C#
    convention.ForTypesMatching(x => x.Name.EndsWith("Exam")).Export();
  3. ForTypesDerivedFrom<>() & ForTypesDerivedFrom(): Allows you to specify a base class or interface that an object inherits from and then exports it. This is the same as using InheritedExport in MEF 1. This is the preferred and most extensible approach when exporting types. This is because it resolves any concrete types that implement the specified interface or inherit its base class. This ultimately means you don’t have to specify the concrete type even when registering the export. MEF will do that automatically. <igreetings><igreetings>This will then find any concrete implementation that implements IGreeting, which in this instance is Greeting.  

    C#
    convention.ForTypesDerivedFrom<IGreetings>().Export<IGreetings>(); 

Working with the API

We’ve taken a brief look at what each method does for registering a type as an export in MEF. Now let’s take that theory and implement it into practice. I’ve created a simple project that I’ve supplied in this article called Learning.MEFII.Console.Exams. The projects class structure is as the following.

 Image 1

The basic scenario is a learner that writes particular exams based on whether they have source material supplied with the exam. This example will illustrate how you can use the ForTypesMatching() method to export and import certain objects based on their type information. As well as a brief look at importing a collection of exports.

C#
class Program
{
    public CompositionContainer container;

    static void Main(string[] args)
    {
        var p = new Program();
        p.RegisterExports();
        var exams = p.container.GetExportedValue<learner>();
    }

    public void RegisterExports()
    {
        var builder = new RegistrationBuilder();
        builder.ForType<learner>().Export<learner>();
        builder.ForTypesMatching
            (x => x.GetProperty("SourceMaterial") != null).Export<exam>();
        InitializeMef(builder);
    }

    public void InitializeMef(RegistrationBuilder registeredExports)
    {
        var assemblyCatalog = 
          new AssemblyCatalog(Assembly.GetExecutingAssembly(), registeredExports);
        var catalog = new AggregateCatalog(assemblyCatalog);
        container = new CompositionContainer(catalog, 
          CompositionOptions.DisableSilentRejection | CompositionOptions.IsThreadSafe);
        container.ComposeParts(this);
    }
}

In the code sample above you can see in the main class Program we register the container as before and a simple export called Learner. However there is a second registered export that is registered in a very different manner. And that is the exportation of the Exam objects.  

C#
builder.ForTypesMatching(x => x.GetProperty("SourceMaterial") != null).Export<Exam>();

You can see that I am only exporting classes that contain the property SourceMaterial. This is a very naïve scenario on how you would determine certain exports based on criteria in a real world application. But this is just a simple example illustrating MEF 2’s ability to filter certain exports. Think of it as LINQifying your IOC container.

Syntactic sugar the cherry on MEF’S cake

The most noticeable bit of flexibility in MEF2 is the addition of predicate filters right throughout the RegistrationBuilder's API as well as the extensive use of chained methods that logically relate to each step of the configuration process to register your desired export. Most of MEF’s lambda expressions relate to .nets built in reflection library which should been seen as an advantage to anyone using MEF. It’s because it solves the problem of learning a whole new reflection library to manipulate MEF’s exports. As I showed in the example above most of the lambda expressions pass in a System.Type argument and returns a Boolean result based on specified criteria. So any type that returns true based on your filter will generate an export. This is a very powerful option to have. It provides you with a far more granular approach to dependency injection. Let’s illustrate this point further with how we can pick what constructor is initialized when an export is imported in MEF.  

Put on that hardhat for some Object Construction

Let me paint a picture for scenario below. The learner has just started high school and will not write an exam until the next term. However the learner has enrolled to study geography and he or she needs a teacher to teach them. So the object looks as follows.

C#
public Learner(IEnumerable<Exam> exams)
{
    Exams = exams;
    Name = "Jack";
    LastName = "Sparrow";
}

public Learner(IEnumerable<Educator> teacher) 
{
    Teacher = teacher.OfType<Teacher>().First();
}

There are two constructors with the same number of parameters. Except for one difference, the learner either just has a teacher to learn a particular subject or is enrolled to write exams. Now what happens if we have an instance of a learner?

Image 2

A Composition error occurs because MEF cannot decide which constructor to use to initialize the learner object. So how do we go about determining which constructor should be used. The RegistrationBuilder’s API has a method called SelectConstructor() that takes a lambda expression to determine the constructor to be used for the objects initialization. So the code is as follows.  

C#
Func<ConstructorInfo[], ConstructorInfo> constructorFilter 
= x => x.First(z => z.GetParameters()
.First().ParameterType == typeof(IEnumerable<Educator>));
            builder.ForType<Learner>().SelectConstructor(constructorFilter).Export<Learner>();

Above we call the SelectConstructor() method and pass in a lambda that accepts an array of ConstructorInfo and returns the correct constructor based on the filter. So in this example we are selecting the first constructor that has a parameter that is of type IEnumerable of Educator and then return it in the lambda expression. Thus the result is the correct constructor is called.

Image 3

Another important thing to note is that if a particular base type is implemented by more than one type it can easily be imported into an objects constructor by just passing it in as an IEnumerable of your designated base type. The great thing about this is you don’t need to do any further configuration in the API to get this to work. There are a few other things mef will do for you straight out the box. To start off when you inject imports into an object which has multiple constructors the one with the most parameters will be selected for constructing the object. Lazy loading can also be done without configuration all you have to do is follow the requirements for a lazy loaded import. And that is done by wrapping the type in a Lazy of T object. Let me show you an example of this below.

 Image 4

There you have it all works by default just by following the conventions I’ve discussed, and with a minimal bit of code being written.

C#
builder.ForType<Learner>().Export<Learner>();
builder.ForTypesDerivedFrom<Educator>().Export<Educator>();

The benefits of being Lazy

The first question to be asked that I showed you in the previous example is. What is the Lazy of T class? And how does it benefit me? Lazy of T is a wrapper class on a type that delays the instantiation of an object until it is needed for use. We all wonder how we can often improve the performance of our applications by minimizing expensive calls to objects or caching information to limit the number of calls to a particular object without instantiating a new instance each time it’s being called. Yes one can create a static singleton of an instance of an object and call it throughout the lifetime of the application. But what happens in the event that an object is only ever called in certain scenarios. For example an application that supports plugins, you should only instantiate the plugin that has been selected by the user not all of them at application startup. A resource should be instantiated only when required. The problem with instantiating all objects regardless of whether they will be used or not is you’ll always have unnecessary objects hanging around in memory and this in efficient and resource intensive.

This is where the Lazy<T> class solves this problem without you having to write a singleton object and then writing some logic to handle the delay of an object being initialized in a thread safe manner. This is all done for you just by wrapping the type in the Lazy<T> generic parameter. How does Lazy of T work? Lazy<T> has two main properties Value and IsValueCreated. The first time the Value property is called an instance of the object is created by using the Activator.CreateInstance() method to dynamically instantiate the object. Thereafter every call to Lazy<T>.Value the currently cached instance is called. The object therefore is only initialized once and IsValueCreated is set to true. A few considerations need to be made when using the Lazy<T> class. Reflection may make your code more flexible and dynamic but it all does come at a cost. All the details related to reflection is only known at runtime so any task you do using reflection is always going to be slower than a direct implementation or call at compile time.

In this scenario a direct call to:

C#
Exam exam = new Exam();

will always be quicker than

C#
Activater.CreateInstance(typeof(Exam));

That being said MEF’s instantiation of all its types is based at runtime so in this scenario all exports are going to be composed at run-time so it makes no difference to pass your imports as Lazy<T> objects. One final unrelated note if you are calling an instance of Lazy<T> in its constructor you can specify how you want a particular object to be initialized.

Image 5

For example

C#
Lazy<Learner> lazyLearner = new Lazy<Learner>(() 
=> new Learner(p.container.GetExportedValues<Educator>().OfType<Teacher>().First()));
            var learner = lazyLearner.Value;

Image 6

There you have it the same result as before but controlling which constructor is used to instantiate the object when calling the Value property. There is another overload that can control the objects thread safety as well by just passing in a Boolean value.

Object Lifetime

In the last few sections we’ve learnt how to construct instances of an object with the import and export api of the RegistrationBuilder with immediate injection as well as deferred injection by way of Lazy<T> implementations. But what about the lifetime of these newly created instances? And how will they be managed? Well let’s address that issue now by turning our focus to the importance of the lifetime of these newly created instances. This is often a neglected thought when dealing with your inversion of control containers and dependency injection, because let’s face it there are those certain scenarios where by creating a singleton of an object is unnecessary and resource intensive.

How the PartCreationPolicy works

In MEF1 you would handle part creation with an attribute on your export. So that when that object is imported in another exported object a singleton of that object would be returned by default. However if you explicitly set the partcreationpolicy to NonShared each time the object was imported a new instance was created. Let’s open the project Learning.MEFII.Console.ObjLifetime to show an illustration of this. The scenario is as follows a LifetimeManager object loops through 4 calls to the exported object the ObjectTester and sets its counter property and then returns the result.

C#
public class ObjectTester
{
    public int Counter { get; set; }

    public ObjectTester()
    {
    }
}

public class LifeTimeManager
{
    public void ShowObjLifetime(CompositionContainer container)
    {
        for (int i = 1; i < 5; i++)
        {
            var tester = container.GetExportedValue<ObjectTester>();
            ++tester.Counter;
            System.Console
           .WriteLine("execution {0} Counter Value is {1}", i,tester.Counter);
        }
    }
}

Image 7

Note the results above show you that no matter how many times the ObjectTester import is called from the container it is never being constructed again after its first time it was instantiated. That is why the counter value increases every time because by default you are always using the same instance. How can we change this? Again we turn to the RegistrationBuilder’s API and after the Export() method you specify the SetCreationPolicy() method. And set its creationPolicy enumeration to NonShared. Like so:

Image 8

Image 9

And now we have our desired result a fresh instance every time an import is called in the loop. This is achieved by just changing a single line of code. This ultimately allows you to not have to worry about changing your code or adding an attribute like in MEF 1. 

To Share or Not to Share

We know MEF2 has the support to alter an objects lifetime but in what scenarios is it useful. And why would we want to override its default behavior of importing singletons into dependent objects? Firstly the most common scenario is DbContexts. When you want a repository to have a new instance of the DbContext each time you execute a query with the database. However what if you want to control when that instance must be instantiated and then disposed of?

The ExportFactory<t>

The ExportFactory<T> gives you better control over an objects lifetime. When you require the instance to be generated and when you want to dispose of that instance. Instead of having the object generated when the parents object is generated you can gain access to the instance only when needed. Like Lazy<T> for example except you can dispose of the instance once execution is done. This is sort of like a scoped import for a unit of work. Let’s look at the example below on how this can be used in a mock db query example. The projects structure looks like the following.

Image 10

What we have above is a simple project mocking an implementation of a repository that gets data out of a DbContext like the entity framework does except the data source in this example is hardcoded.

C#
public class PersonRepository<TEntity> : IRepository<TEntity>
    where TEntity : class, IPerson
{
    public ExportFactory<IMockDbContext> DbContext { get; set; }

    public PersonRepository(ExportFactory<IMockDbContext> dbContext)
    {
        DbContext = dbContext;
    }

    public TEntity GetByAge(Func<IPerson, bool> filter)
    {
        using (var ctx = DbContext.CreateExport())
        {
            return ctx.Value.MockPersonDbSet.AsEnumerable().SingleOrDefault(filter) as TEntity;
        }            
    }
}

The first important thing to note above is the the IMockDbContext object is wrapped in a ExportFactory<T> object and then injected into the Person Repository class. And then when GetByAge method is called. A using statement is wrapped around the ExportFactory<T> type and CreateExport() method is called. This immediately shows you the export implements IDisposable and will dispose of the export when execution completes in the code block. The object is only initialized when the Value property is called on the ExportLifetime<IMockDbContext> object ctx. The object is then disposed of when it has finished getting the oldest person from the MockDbContext, by courtesy of the using block.

C#
public class ProgramManager
{
    public IRepository<IPerson> PersonRepo { get; set; }

    public ProgramManager(IRepository<IPerson> personRepo)
    {
        PersonRepo = personRepo;
    }

    public void SimulateUserAction()
    {
       var person = PersonRepo.GetByAge(x => x.Age > 21);
       System.Console.WriteLine("The oldest person in the table is {0} at the tender age of {1}", person.Name, person.Age);
       System.Console.ReadKey();
    }
}

Why use ExportFactory<T>?

The benefits of the ExportFactory<T> come down to the architecture of your application as well as your applications defined requirements. The ExportFactory<T> for example would be used to minimize the resource intensity of object creation by only calling the object when use of that object is required and also to dispose of that object once it has been used. The Lazy<T> is a great example of deferred creation of an object, however you do not have finer control when it comes to the objects lifetime. Once the object has been initialized when the Value property is called on Lazy<T>, it stays in memory throughout the lifetime of the application. The ExportFactory<T> differs slightly in that it defers the instantiation of the object, and also provides you with controlling the disposal of the objects instance.  

Export Multiple Interfaces

MEF 2 preview 5 now supports the exportation of multiple interfaces on an implemented concrete type. By way of the ExportInterfaces() method in the RegistrationBuilders Api. There are three method overloads one for exporting all implemented interfaces, the second overload is used to only export interfaces that meet certain criteria and finally the third overload takes a lambda that specifies the ExportBuilder as well as the interface filter.

Image 11

Image 12

Image 13

In the example below we will be using the first and second overload. Please not the project we are using is called. Learning.MEFII.Console.MultipleInterfaces 

 Image 14

Here we have a simple program that shows human characteristics made up of segregated interfaces by each relating characteristic. The Person class implements all these interfaces as they all relate to a Person object.

The code is as follows.

C#
public class Person : IHuman, IMammal, IHomosapien
{
public int NoLegs { get; set; }

public HairType HairType { get; set; }

public long IdNumber { get; set; }

public string Name { get; set; }

public Person()
{
    NoLegs = 2;
    HairType = MultipleInterfaces.HairType.Hair;
    IdNumber = 8404295531083;
    Name = "Butch";
}

public string BloodType { get; set; }
}
static void Main(string[] args)
{
    var p = new Program();
    p.RegisterExports();
   var humanCharacteristics = p.container.GetExportedValue<IHuman>();
   var mammalCharacteristics = p.container.GetExportedValue<IMammal>();
   System.Console.WriteLine("Human Characteristics Name: {0} Id Number: {1}", 
          humanCharacteristics.Name, humanCharacteristics.IdNumber);
   System.Console.WriteLine("Mammal Characteristics No of Legs: {0} Type of Hair: {1}", 
          mammalCharacteristics.NoLegs, mammalCharacteristics.HairType);
   System.Console.ReadKey();
}

public void RegisterExports()
{
    RegistrationBuilder builder = new RegistrationBuilder();
    builder.ForType<Person>()
           .ExportInterfaces();
    InitializeMef(builder);
}

As you can see all interfaces that implement the person object are being exported. So when the exported value is requested of the specified contract type eg interface. Only the properties and methods relating to that implemented interface are exposed to the client from the Person object. Please note that even though I am exporting and retrieving the value of multiple interfaces. The object is still only created as a singleton by default. By default the first exported interface to call the concrete type will instantiate the object.  Eg;

 Image 15

Conclusion

This article has gone through the many new exciting features that will most likely be released in the next iteration of MEF which comes out later this year with .NET 4.5. The most notable changes was the removal of attribute driven injection and a far richer api to work with when it came to defining exports and injecting imports. It also provides a lot more flexibility when it comes to the criteria that are set in defining types that are eligible to be registered in the CompositionContainer. The MEF project has grown from strength to strength; so the future looks bright for MEF and the .NET framework. So watch this space. 

Points of Interest

Check this link out.

http://blogs.msdn.com/b/bclteam/archive/2011/12/19/what-s-new-in-mef-2-preview-5-alok-nick.aspx

History 

Version 0.1 By Dean Oliver

License

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


Written By
Software Developer BBD Johannesburg
South Africa South Africa
Bsc (Hons) Business Information Systems.
MCTS: Web Applications Development with Microsoft .NET Framework 4
MCTS: Windows Communication Foundation Development with Microsoft .NET Framework 4
MCTS: Accessing Data with Microsoft .NET Framework 4
Microsoft Certified Professional Developer Certification.

Comments and Discussions

 
QuestionMissing dlls Pin
yotta25-Jan-16 22:51
professionalyotta25-Jan-16 22:51 
QuestionWindows Store Example? Pin
Member 1192656930-Aug-15 19:34
Member 1192656930-Aug-15 19:34 
AnswerRe: Windows Store Example? Pin
Member 1192656930-Aug-15 20:35
Member 1192656930-Aug-15 20:35 
SuggestionImages Pin
Eugene Sadovoi18-Apr-12 10:35
Eugene Sadovoi18-Apr-12 10:35 
AnswerRe: Images Pin
Dean Oliver18-Apr-12 18:48
Dean Oliver18-Apr-12 18:48 
GeneralRe: Images Pin
Eugene Sadovoi19-Apr-12 4:08
Eugene Sadovoi19-Apr-12 4:08 
GeneralRe: Images Pin
Dean Oliver19-Apr-12 4:26
Dean Oliver19-Apr-12 4:26 
I've tried on 4 different pc's. I think the problem is on your browser.
BugRe: Images Pin
Eugene Sadovoi19-Apr-12 4:32
Eugene Sadovoi19-Apr-12 4:32 
Questionnice Pin
BillW3318-Apr-12 9:24
professionalBillW3318-Apr-12 9:24 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.