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

From Zero to Proficient with MEF

Rate me:
Please Sign up or sign in to vote.
4.99/5 (264 votes)
21 May 2012CPOL40 min read 366.2K   8K   375   132
Learn how to go from being an absolute beginner in the Managed Extensibility Framework to being an advanced user.

Introduction

Why is it that when we look for a new laptop, we look at the types of ports it has? Not only do we put up with these holes in the sides of our new laptops, we complain if there aren’t enough of them. The answer, of course, is that these ports allow us to extend our laptop. We can add a second monitor, an external hard drive, or a number of other devices. This doesn’t mean that the original laptop is inferior; it just means that different use cases lend themselves to different configurations. So why is it that we insist on making applications without “ports”?

In .NET 4.0, Microsoft provided us with the Managed Extensibility Framework (MEF). This framework allows us to easily create extensibility points (ports) in our applications. The most obvious use for this is for plug-ins. You could allow a customer to create their own menu items just like they can do in Microsoft Word or Visual Studio. However, there are other uses for MEF as well. For example, if you expect business rules might be changed or expanded in the future (that never happens, right?), you could use MEF to make this process simple. I’ll show you how below.

Why Should I Care About MEF 

MEF has a lot of similarities to an IoC, but there are some differences.  MEF concentrates on bringing parts into your application, even if the implementations are not known or local, whereas IoC is more about loosely coupling known parts.  That still leaves the question of when to use MEF.  The simplest explanation would be to say that it is for when you want to allow plugins for your application.  However, I think there are a lot more reasons to use MEF.  As I see it, the major reasons to use MEF are:

  • To enable users to develop their own plugins for your application.
  • To allow others to extend or modify how your application works without getting access or changing the source.
  • Easy testability (just change where the DLLs are coming from and you could use your app against an entire set of mocks).
  • To loosely couple library projects to your application, especially when they are used by multiple projects. 

This last reason is a fairly broad one.  Let me give you an example.  Say you create a standard way to access your database in a safe manner.  Maybe you have some custom data access business logic.  In the end, you have an Interface that exposes two methods – ReadData and WriteData.  You want use this DLL in all of your applications that you build internally.  You could put the DLL in the GAC of every server or you could attach it to each project.  Either solution has its merits but now you have a third option.  You could store it centrally and load it via MEF.  Then you could easily change it whenever you needed to without a big deployment issue (as long as you didn’t change the interface, which you should never do). 

The bottom line is that there are a lot of reasons to use MEF.  As you get comfortable with MEF, you will start to see more and more places in your applications that could be made even better.  This is one tool you really want in your toolbox. 

Target Audience

When I first started learning how to use MEF, there were a number of resources that helped me. I found practical articles that showed me how to use the framework in a real application and I found technical articles that explained certain features in great length. What I never seemed to find was that one article that was both practical and in-depth. I ended up going from source to source, picking bits and pieces from each.

This article is intended to be that in-depth look at what MEF is in a practical manner. I will attempt to walk you through MEF from start to finish. Along the way I will build example applications so you can see exactly how each feature works. For those of you who are familiar with MEF, this article is broken up by feature so that you can quickly get to just the area you need help with.

Example Application

I have developed an example application that goes over each of the topics below. I have tried to keep the examples simple and easy to follow, yet practical. In the attached code section, you will find a solution that contains eight projects. At the beginning of every section, I will tell you which project to find the examples for that section. Each project also has text at the beginning that tells you what sections it covers. Finally, the parts of the code that deal with a particular section are also labeled with the section name. For the project Example05 (dealing with external libraries), I created two additional projects (Common and ExternalLibrary).

The Theory

MEF is used to design “ports” in your application. It can create both single-use ports (think VGA port) as well as multi-use ports (think USB ports). It also specifies how to create the “plugs” that go into those ports. It sounds complicated but it is really not much more than properly using interfaces and a little markup. I could go into a long-winded explanation of how it is designed and what it can do, but that can be hard to follow. Instead, let’s dive right into how to use MEF and then we can come back to the theory.

The Basics

There are three basic parts to MEF in your application. If we continue on with our laptop analogy, we can visualize these three parts as the USB port on the laptop, an external hard drive with a USB connector, and the hand that plugs the USB connector into the port. In MEF terms, the port is defined as an [Import] statement. This statement is placed above a property to tell the system that something gets plugged in here. The USB cable is defined as an [Export] statement. This statement is placed above a class, method, or property to indicate that this is an item to be plugged in somewhere. An application could (and probably will) have a lot of these exports and imports. The job of the third piece is to figure out which ports we have and thus which cables we need to plug into them. This is the job of the CompositionContainer. It acts like the hand, plugging in the cables that match up to the appropriate ports. Those cables that don’t match up with a port are ignored.

Simple Example

Example Project Name: Example01

Let’s look at a simple example. If you have done any research into MEF, you have probably seen this type of example before but we need to crawl before we can walk. This application will have a string that imports a value at runtime. We will export a message to be put into the string and then we will display the string to show it works.

To follow along with this example, simply start a new C# project of type “Console Application” in Visual Studio. When the project is created, you will have a “Program.cs” file. Inside that file, you should see the following code:

C#
static void Main(string[] args)
{
}

Since this is a static application, we need to create an instance of it instead so that we can play with MEF. Create a new void method called Run and then add code inside the Main method to instantiate the program and call the Run method. Your code should now look like so:

C#
static void Main(string[] args)
{
    Program p = new Program();
    p.Run();
}

void Run()
{
}

So now we have the plumbing set up. There is nothing MEF-specific in these lines, so I wanted to be sure that this code gets separated from our MEF code for the sake of clarity. Now let’s get to the steps needed to get a basic MEF implementation working.

Step 1: You will need to add a reference to System.ComponentModel.Composition in your application. This is where MEF lives. This is dependent on .Net 4.0 framework. In theory you can download it from Codeplex and get it to (mostly) run on .Net 3.5 but that isn’t supported or recommended.

Step 2: Add a using statement in your Program.cs file that references System.ComponentModel.Composition. This will allow us to use the Import and Export statements directly. It will also allow us to call the “ComposeParts” method on our container later (for now just remember this information – it will make sense later on).

Step 3: Add a class-level variable of type string called message. On top of this variable, add a [Import] statement. Your code should look like this:

C#
[Import]
string message;

This is our “port”. We are asking our hand to find us a plug that fits into a string variable.

Step 4: Add a new class outside the Program class (it can be in a different cs file or in the same one – I put ours in the same file just for simplicity). Call it MessageBox. Inside the class, add a string property called MyMessage that returns an example message when the get is called. On top of the property, add [Export]. Your class should look like this:

C#
 public class MessageBox
{
    [Export()]
    public string MyMessage
    {
        get { return "This is my example message."; }
    }
}

The MyMessage property is our “plug” that will be put into the string “port” we defined above. We have defined the port and the plug, but we aren’t done yet. If we stopped now, the application would run but nothing would happen. The reason is that we have not yet defined the method that will put the plugs into the ports. That comes next.

Step 5: This is the step where people new to MEF can get lost because there are actually a couple steps in one. I’m going to walk you through each part of this step so you know exactly what it going on. In the end, we will put all of the code from this step into one helper method called Compose. This will make it easy for our application to hook everything up in a clean manner. To make this easier, we should also add a using statement for System.ComponentModel.Composition.Hosting. This will make the code to follow more concise.

First, we need to create a catalog of all of the exports and imports. This will simply be an inventory of what is being exported and what needs to be imported. The catalog will need to know where to look. In our case, we will point to the executing assembly. This tells the catalog to inventory our current application. This may seem redundant but don’t forget that MEF is designed to load external resources as plug-ins. In a more complex application, you would need to create multiple catalogs. For now, though, we will just create the one catalog like so:

C#
AssemblyCatalog catalog = new AssemblyCatalog(System.Reflection.Assembly.GetExecutingAssembly());

Next, we need to put this catalog into a CompositionContainer. This will give us the methods we need to compose (hook up) our exports and imports from our catalog. The code looks like this:

C#
CompositionContainer container = new CompositionContainer(catalog);

Finally, we need to actually hook everything up. This can be accomplished in this case by telling our new container to either compose the parts (ComposeParts) or satisfy the imports once (SatisfyImportsOnce). In either case, we need to pass in the instance that needs to have the imports satisfied on (where the ports are that need to be plugged in). We will do that by passing in the this keyword, which tells the method to look at our current instance of Program for the imports statements. Because we are using such a simple example, either of the composition methods listed would work. The difference is that the SatisfyImportsOnce statement will only hook up the parts once. That means that any changes to our parts catalog (either adding parts or removing them) won’t be put into our application automatically. We will get into this more advanced topic later. For now, we are going to use the simplest implementation like so:

container.SatisfyImportsOnce(this);

So now we have our helper method. If you have been following along, you should have a method that looks like this:

private void Compose()
{
    AssemblyCatalog catalog = new AssemblyCatalog(System.Reflection.Assembly.GetExecutingAssembly());
    CompositionContainer container = new CompositionContainer(catalog);
    container.SatisfyImportsOnce(this);
}

Step 6: The final thing we need to do to get all of this to work is to get up our Run method to call the Compose method and then display the value of our message variable. Because this is a console application, I will also put a Console.ReadLine at the end of the method so that the application doesn’t close until we hit a key. Here is the completed Run method:

C#
void Run()
{
    Compose();
    Console.WriteLine(message);
    Console.ReadKey();
}

That is all it takes to create a simple MEF application. If you run this application right now, you will see the message “This is my example message.” on the screen. If you got something different than I did or if you want to just see the end results in a working file, see Example 01 in my attached code.

To summarize, we created a new console application that had one “port” of type string called message. We designed a “plug” of type string that returned the message “This is my example message.” We then set up a container that took our catalog of parts and hooked them up (once). This allowed us to display the value of our message variable, which was populated with the value pulled from our “plug”.

Generated Questions

There are a lot of simple MEF demos that wrap things up at this point, but for me this demo generated a lot of questions so before we proceed, let’s answer a few of these questions.

  1. When I perform the SatisfyImportsOnce or ComposeParts method, am I creating an instance of every item I need to import right away? Yes.
  2. Funny. Care to expound? In most cases, creating an instance of every import right away will be fine. However, in the event that you might not use all of the instances or you want to load them later on, you can use lazy load on your imports. We will get into that below.
  3. If I export a class and then import it in two locations, do I get two instances of the class? By default, MEF treats each export as a Singleton… well, sort of. If all the defaults are on, you get a singleton. See the “Object Instancing” section for a more detailed description of how the defaults work.
  4. Can I create “regular” instances of classes that are marked for export? Yes, you can. Just note that any import statements that are inside the class will not be satisfied since the instance was created after MEF satisfied the imports. You could change this by passing in the instance reference instead of the “this” keyword in the line container.SatisfyImportsOnce(this);
  5. If I’m using MEF, should I use it everywhere in my application? If you had a hammer, would you use that for all of your home repair needs? If so, you probably aren’t any better at home repair than I am. To directly answer your question, no, MEF is just one tool you can use in your application. It is designed to handle one set of requirements (plug-ins) very well. It can do other things (IoC/DI) but that isn’t what it was designed to do. MEF, like a hammer, can do a lot but sometimes it is better to use a different tool.
  6. What do I do if I want a bunch of one object? There are a couple ways to handle this, depending on what you want. We could do different interfaces (if you wanted the same object to be used differently in different parts of your application), we could import it into multiple variables (if we wanted to use the same object in multiple locations in our code) or we could use an ImportMany (if we wanted multiple different objects with the same signature). We will see all of these solutions below.

Allowing null Imports

Example Project Name: Example02

Sometimes you may want to have an import that might or might not have a matching export. In the example of our laptop, we wouldn't want the laptop to violently blow up if we didn't have every USB port filled. Yet, if we don't change any of the defaults, that is exactly what our application will do if we don't fill all of our import statements. We can have as many non-matching exports as we want, but the first import statement that doesn't have a matching export will throw an exception. If this isn't what you want, there is a way to change the default behavior. On an import statement, you can put named parameters at the end to modify how the import works. One of these parameters is called AllowDefaults. If you set this to true, MEF will attempt to satisfy the import statement, but failing that, it will put a null value in the variable instead. The import would look like this:

C#
[Import(AllowDefault = true)]
string message;

In your code, make sure you then test the variable to be sure it isn't null before you use it. Otherwise, you will have just shifted your application-killing exception to a little later in your application's life.

Object Instancing

Example Project Name: Example02

By default, as we said before, MEF creates each export as a Singleton. This is technically true and a lot of descriptions stop there. However, the truth is a bit more complicated. In MEF, there are three PartCreationPolicy values that can be specified. The three values are Shared, NonShared, and Any. By default, Any is what is used on all exports. If the Import statement does not specify the type of PartCreationPolicy it is looking for, the Any policy will translate to Shared. A Shared policy is a Singleton and, of course, Non-Shared indicates that a new instance will be created for the export each time it is imported. Confused yet? How about some code to make this a little easier to understand? I’ll put an export and an import statement and then explain what would happen.

Example 1: First, we will look at the simplest example just to get our feet wet:

C#
[Export, PartCreationPolicy(CreationPolicy.Any)]
public class MyClass {…}

[Import]
MyClass _port1;

[Import]
MyClass _port2;

In this example, the _port1 and _port2 variables will get the same Singleton instance of the MyClass object. The export statement is functionally equivalent to just [Export]. I just explicitly set the PartCreationPolicy to show you what was happening by default.

Example 2: Next, let’s look at how we could change the export statement to create an instance each time we import it:

C#
[Export, PartCreationPolicy(CreationPolicy.NonShared)]
public class MyClass {…}

[Import]
MyClass _port1;

[Import]
MyClass _port2;

This time, _port1 and _port2 will get separate instances of the MyClass object. The creation of the instances is done by reflection, which can be a bit costly. There are ways to reduce this cost, but they are outside the scope of this already large article.

Example 3: I’ve already hinted above that you can specify what type of PartCreationPolicy an import will accept. Let’s see how to do that:

C#
[Export, PartCreationPolicy(CreationPolicy.Shared)]
public class MyClass {…}

[Import(RequiredCreationPolicy = CreationPolicy.Shared)]
MyClass _port1;

[Import]
MyClass _port2;

In this case, the _port1 variable is requiring that the export be Shared. Because our export is Shared, _port1 will get the Singleton instance of the MyClass object like normal. The _port2 variable will also get the same Singleton instance. If we changed our export to be NonShared however, we would get an error on the _port1 variable because no exports would match the required import.

Example 4: This brings us to the question we asked originally about the default export being a Singleton but that the import also had to be default for that to be true. Let’s look at an interesting example below:

[Export, PartCreationPolicy(CreationPolicy.Any)]
public class MyClass {…}

[Import(RequiredCreationPolicy = CreationPolicy.Shared)]
MyClass _port1;

[Import(RequiredCreationPolicy = CreationPolicy.NonShared)]
MyClass _port2;

I bet you are wondering what happens here. I specified the Any on the export again just so you could see it. We could have just said [Export]. When the _port1 variable asks for an object with a Shared part creation policy, it gets a Singleton instance of the MyClass object. However, when the _port2 variable asks for an object with a NonShared part creation policy, it gets a new instance of the MyClass variable. By specifying a PartCreationPolicy of Any or by not specifying any policy, the export becomes whatever the import wants it to be. If nothing is specified on the import, you will get a Singleton. Make sure you understand this. This is where you could run into trouble down the road if someone decides to specify the RequiredCreationPolicy of NonShared on an import when you were expecting your export to always be a Singleton, even though you only labeled it as [Export].

Specifying Export Types

Example Project Name: Example02

When you mark an object as an export, by default MEF looks at the type of object it is and matches it up to an import that needs that type. Thus when we export a string, it gets matched up to an import of type string. However, this becomes problematic in anything other than an example application, since you may want to export multiple objects of type string. If there are multiple exports that could satisfy an import, MEF will throw an error because it doesn’t know which export to use (see Gotchas below). There is a way to put multiple export instances into one import by using ImportMany (see below for more information on this topic) but most likely we want to match up specific exports to specific imports. The way you do this is by describing this export to MEF. You can do that in a number of ways. The first way is to specify a contract name. This can be any name you choose. Here is an example:

C#
[Export("MyConfigInfo")]
public class MyClass  {…}

[Import("MyConfigInfo")]
object _port1;

There are a couple things to note here. First, I used a magic string to identify my export. I needed to use the same magic string to identify the import. If I didn’t use the “MyConfigInfo” on the import statement, MEF would have thrown an error saying it couldn’t find the export I was looking for (see Gotchas). Second, I changed the type for _port1 to show that the types don’t need to match anymore. Since we are explicitly saying how the import and export should know each other, the type of the variable doesn’t matter. That doesn’t mean you can put a string into an int variable. That still throws an error (can I get a collective “duh” here?). I recommend against using a contract name to identify an export. The reason being that you are reliant on typing everything correctly and you cannot use Intellisense. However, that is just a general rule of thumb I follow. There are cases where it is the best solution. As always, do what is best for your environment.

We can also identify objects by type in our Export/Import. This way does make use of Intellisense and it can be a lot cleaner (in my opinion, but feel free to harass me mercilessly below should you disagree). When you are exporting class objects, you can make use of Interfaces. Here is an example:

C#
[Export(typeof(IConfigInfo))]
public class MyClass : IConfigInfo {…}

[Import(typeof(IConfigInfo))]
object _port1;

That is pretty straightforward. Basically, if you are familiar with programming to an interface, you are already set. In your export statement, just specify the type you want to identify the export by and then do the same for the import.

If you really want to get specific, you can specify both a contract name and a contract type in your Export/Import. Basically it takes the two scenarios above and combines them. The end result would look something like this:

C#
[Export("MyInfo", typeof(IConfigInfo))]
public class MyClass : IConfigInfo {…}

[Import("MyInfo", typeof(IConfigInfo))]
object _port1;

One question that might occur to you is what will happen if you export both the contract name and contract type but only import based upon one of the criteria. The answer is that it won’t work. If you are specific in the export, you have to have an exactly matching specificity in the import. The one exception to this is when you don’t specify the type in either the export or the import. Since MEF actually implies the type, you can be implicit in one and explicit in the other and the connection will still work.

OK, make sense so far? Great, because we are going to make it more complicated. Up until now we have talked about exporting objects like classes and value types. There is one area we haven’t touched on yet, and that is the area of methods. MEF allows us to export and import methods in the same way we do with entire classes. Just like with everything else, you can specify a contract name and/or contract type. One thing that is a little different here is that we can use a delegate like Action or Func to specify the contract type. Here is an example that uses both a contract name and contract type:

C#
[Import("ComplexRule", typeof(Func<string, string>))]
public Func<string, string> MyRule{ get; set; }

[Export("ComplexRule", typeof(Func<string, string>))]
public string ARule(string ruleText)
{
    return string.Format("You passed in {0}", ruleText);
}

To call the rule, you would do something like this:

C#
Console.WriteLine(MyRule("Hello World"));

I made sure this example was a bit more advanced so you could see exactly how this would work. If you haven’t used delegates and anonymous types before, you might need to brush up on them before moving forward with this part of MEF. Trying to learn two technologies at once and using them together is a recipe for a debugging disaster.

So what we are doing here is exporting a method that has one argument (of type string) and an output of type string. We are importing it into a property and calling it as if it were a local method. We also applied a custom name to our method export so that we can differentiate it from other methods with a similar signature.

Inheriting an Export

Example Project Name: Example03

One feature that really makes MEF powerful is the idea of InheritedExport. With this feature, you can ensure than any class that inherits from the item marked with [InheritedExport] will be exported. This works just like an export statement, so you can have a contract name and/or contract type specified. Before we get into the benefits of this, let’s look at an example:

C#
[InheritedExport(typeof(IRule))]
public interface IRule


public class RuleOne : IRule {…}
public class RuleTwo : IRule {…}

Even though RuleOne and RuleTwo don’t have an explicit export statement, they will both be exported as IRule types (by the way, read that interface name over and over until you feel better about yourself – I’m not just a coder, I’m also a self-help coach). Now, look over that scenario for a minute and think about the possibilities. First, if you put your Interface in a different assembly, the assembly where RuleOne and RuleTwo were located would not need to know about MEF. You are also guaranteed that the export name and type are correct. Finally, you (mostly) ensure that part of the contract for implementing the interface is that the concrete class gets exported as a part. I say mostly because there is one keyword that can be used to block the export. You can use the [PartNotDiscoverable] tag on the top of your class to specify that this class should not be picked up by the MEF library. The class can still be added explicitly (we will get to that later) but it won’t be picked up by the ComposeParts or SatisfyImportsOnce methods.

Importing Multiple Identical Exports

Example Project Name: Example03

If you were to test out the above example, you would probably run into an error that says “More than one export was found that matches the constraint” (see Gotchas). This is because you have one import statement and more than one export statement that matches your import. In terms of our analogy, we have two VGA cables but only one VGA port. Our hand doesn’t know which one to plug in. Sometimes this is a mistake on our part. We define two parts to do the same job. Other times, however, we want this to happen. A common example would be a set of rules that needs to be run. It would be highly unlikely that you would have only one rule for any given scenario. Instead, you would probably have many rules. This is where the [ImportMany] statement comes in. It allows us to import zero or more Exported items that match. Did you catch that? An ImportMany statement won’t fail if you don’ have any matching export statements. It will, however, initialize the collection that you have the ImportMany statement on. That prevents errors where a collection wasn’t initialized when it was called. Let’s see how this works:

C#
[ImportMany(typeof(IRule))]
List<IRule> _rules;

This ImportMany statement will work with the above InheritedExport example. Now you can use a foreach on the _rules list and call each rule individually.

Delaying Instance Creation

Example Project Name: Example03

Earlier we talked about the fact that MEF creates instances of every needed import when ComposeParts or SatisfyImportsOnce is run. Normally this should be fine. Newly created instances of classes usually don't take up that much memory. However, there may be times when you want to delay the instantiation of an object or set of objects. To do this, simply use the Lazy<T> for delayed instantiation that Microsoft has already provided us. Nothing changes on the export, only the import needs to be changed like so:

C#
[Import]
Lazy<MyClass> myImport;

Note that this isn't really MEF technology, but rather MEF using Lazy<T> effectively. It isn't a MEF-specific implementation. Don't forget that when you lazy-load something, you need to access the actual instance you are looking for using the Value property. In our case, that would look like this:

C#
myImport.Value.MyMethod()

Using lazy-loading to delay instantiation of certain classes definitely has its place, especially if you are uncertain if all of your imported instances will be used or if certain imports are resource-intensive. Again, just because you found a new tool doesn't mean that it is the best tool for all jobs.

Describing the Export

Example Project Name: Example04

Now that we have learned about how we can use ImportMany to bring a bunch of matching exports into one array and how we can wait to instantiate our imports until we need them, the issue of export decoration comes up. How do we differentiate one export from another if the signatures match without peaking inside them? The answer is metatdata. We can attach metadata to our exports to describe information about the export to the importing application. For instance, say we were going to import a number of business rules. Each rule has the same signature, but deals with how to process a record in a different state. This might be an excellent opportunity to use lazy-loading because we most-likely will not use the rule for every state. It is also an excellent opportunity to use metadata, because we can describe which state each rule handles. Let's look at an example. Here is our exported class objects:

C#
public interface IStateRule
{
    string StateBird();
}

[Export(typeof(IStateRule))]
[ExportMetadata("StateName","Utah")]
[ExportMetadata("ActiveRule", true)]
public class UtahRule : IStateRule
{
    public string StateBird()
    {
        return "Common American Gull";
    }
}

[Export(typeof(IStateRule))]
[ExportMetadata("StateName", "Ohio")]
[ExportMetadata("ActiveRule", true)]
public class OhioRule : IStateRule
{
    public string StateBird()
    {
        return "Cardinal";
    }
}

I added two pieces of metadata to each export just to show you that you can stack these up. I will only do my filter based upon one piece of data (StateName) but I could filter these exports based upon which were active as well. That way we could have multiple exports for the same state. It would be even better if we put an activation date that we could filter on so we could always get the latest version yet the older versions could be retained in case we needed them to process old records, etc.

Here is how we would import those class objects:

C#
[ImportMany(typeof(IStateRule))]
IEnumerable<Lazy<IStateRule, IDictionary<string, object>>> _stateRules;

Notice that we are using lazy-loading, [ImportMany], and export metadata all against on variable. Whew. That is a lot of different items crashing together in two lines of code. If we hadn't already gone over each of these concepts, it would probably be very confusing but I'm sure it is simple to you now. Just in case it isn't, however, let me explain a bit. We are doing an ImportMany so we can bring in multiple items with the same signature. In our case, the signature is that the export will be of type IStateRule. We are putting this set into an IEnumerable so that we can cycle through the set of imported rules. Next, we are specifying that the items will be Lazy<T> so that they are not loaded right away but rather only when called. Finally, after the type of item to import is specified (IStateRule), we add a parameter to specify an IDictionary set. This is the metadata declaration.

Now that we have set everything up, here is how we would access the metadata. In this example, I am looking to only call the Utah rule:

C#
Lazy<IStateRule> _utah = _stateRules.Where(s => (string)s.Metadata["StateName"] == "Utah").FirstOrDefault();
if (_utah != null)
{
    Console.WriteLine(_utah.Value.StateBird());
}

The first line in the above part is a simple Linq query that gets me the first item that matches the criteria specified. In this case, I specified that the "StateName" value should be "Utah". That will put the Lazy<IStateRule> copy into my local variable _utah for use. I did not try to get the actual instance of IStateRule inside the Linq query because if the value isn't found, the system will throw an error.

Normally I try to keep examples as simple and focused as possible, so I avoid standard error handling and other plumbing. However, in this case I thought it wisest to include some basic error handling capabilities. Since we are using simple string keys in our Dictionary, there is a very real possibility that we might not have the correct pair listed in every export. What I did here was I made sure to use the FirstOrDefault method so that it won't fail if the query doesn't return any data. I also then check to be sure the _utah variable isn't null before I try to call it.

<<hand raises>>

I see that hand, but I alread know what you are going to ask: "Why aren't you using [InheritedExport] in this example?" I was hoping you wouldn't ask that, because I don't have a very satisfactory answer. Since you did, however, I'll give you what I have. When you use the [InheritedExport] tag on the IStateRule interface in this example, the metadata does not get exported with the exports. From what I can tell, this is because the metadata needs to be exported at the same time as the export itself and the [InheritedExport] tag seems to be processed at a different time. I have tried multiple tricks to get it to work to no avail. If you know how to get this to work (without a workaround), please let me know. I'll be sure to post your solution here and give you the credit.

Advanced Metadata

Example Project Name: Example04

To make metadata even more useful, instead of using the Dictionary<string, object>, you can specify your own class or interface. The easiest way to do this is on the import side of the application. You simply need to replace the Dictionary<string, object> with your interface or class name like so:

C#
[ImportMany(typeof(IStateRule))]
IEnumerable<Lazy<IStateRule, IStateRuleMetadata>> _stateRules;

My interface looks like this:

C#
public interface IStateRuleMetadata
{
    string StateName { get; }
}

One thing to note here is that I made my property read-only (only a getter). This is intentional. MEF will throw an error if you try to include a setter. There is no need for a setter, however, so this is fine. To access this metadata, you use the metadata dot property like so:

C#
Lazy<IStateRule> _utah = _stateRules.Where(s => (string)s.Metadata.StateName == "Utah").FirstOrDefault();

As you can see, my code on the import side no longer uses strings for the key names, but property names. This allows me to utilize Intellisense at design time, which makes my development easier and it makes it easier to avoid errors that will be difficult to debug.

I am sure you are wondering what has changed on the export side. The answer is that nothing has changed. You still specify the metadata properties using strings (no Intellisense). MEF handles how to hook these into your metadata class or interface at runtime. This is especially cool if you use an interface for your metadata like I did, since your interface is never actually implemented in your code. MEF implements it for you.

Loading Other Assemblies

Example Project Name: Example05

So far we have concentrated on how to get the exports from our current assembly to match up with our imports. MEF provides us with a lot more options than this, which is important. Let's look at the different options we can use.

Assembly Catalog

This is the catalog we have been using up until now. You pass in a specific assembly and MEF handles the discovery of the parts inside that assembly. In our case, we have been passing a reference in to the current assembly (Assembly.GetExecutingAssembly).

Directory Catalog

This is the fun one. It takes in either a relative or absolute path to a directory. MEF will then scan that directory (but not the sub-directories) for assemblies that are decorated with MEF exports. It will add any parts it finds to the catalog. You can further refine this by adding a parameter that specifies the format for the name of the assembly. For example, you could put "*.dll" to include only dll files or you could put "CompanyName*.dll" to include only dlls that start with your company name. Here is an example of how to set up a directory catalog using a relative path and a filter:

C#
DirectoryCatalog catalog = new DirectoryCatalog("Plugins", "*.exe");

I decided to search for an exe file just to show you that it does not have to be a dll. Note that the folder name does not need to include any leading characters like slashes or dots.

Type Catalog

This is one I personally do not use. The purpose of this catalog is to only load exports of a specified type in the catalog. To specify the types, you need to add them directly to the constructor line. For sake of completeness, I will include an example of doing so here:

C#
TypeCatalog catalog = new TypeCatalog(typeof(IRule), typeof(IStateRule));

Notice that I added two types, but more could be added as needed.

Aggregate Catalog

When having one catalog isn't enough, you can create an aggregate catalog. This catalog is simply a catalog of catalogs. You can add as many catalogs as you want to this catalog. Simply pass in your catalogs to the constructor and you are set. Most people create their individual catalogs in the constructor line of the aggregate catalog for simplicity and so that they don't have to create individual variables for each catalog. I will show you this technique:

C#
AggregateCatalog catalog = new AggregateCatalog(new AssemblyCatalog(Assembly.GetExecutingAssembly()), new DirectoryCatalog(@"C:\Plugins"));

As you can see, I am creating two different types of catalogs (DirectoryCatalog and AssemblyCatalog) inside the AggregateCatalog. This will allow my system to discover the internal parts as well as the parts from all of the assemblies inside the temp folder. Note that I used the string literal character (@) to prevent me from needing to escape the slashes inside my folder path.

Adding New Assemblies at Runtime

Example Project Name: Example05

Now that we know how to load all of the assemblies inside a folder with MEF, the idea of adding assemblies on the fly shouldn't be far behind. This will allow us to drop new dll files into our folder and have our currently-running application pick them up and use them.

To accomplish this, there are a few things we need to bring together. First, we are going to need to use the DirectoryCatalog. This is where the changes will be picked up (since we won't be changing our current assembly code on the fly). Next, we will need to create a variable to hold the directory catalog instead of creating a new instance inside the AggregateCatalog constructor. We will also need to be sure to scope this variable in such a way as to have access to it later on. Finally, we are going to need to have some sort of trigger to tell us when we should tell the DirectoryCatalog to update (and thus trigger a recomposition).

In a real-world application, you would most likely put a FileSystemWatcher on your plugins folder and have the events from that trigger the DirectoryCatalog. Doing this is outside the scope of this article. Instead, I will show you the code you need on the MEF side. How you trigger that code is up to you.

The actual implementation of recomposition is fairly simple. First, you need to be sure you use the ComposeParts method instead of the SatisfyImportsOnce method. Then you need to mark any Import or ImportMany statement that might be getting a new part with the parameter "AllowRecomposition=true". Then you either add something new to the CompositionContainer or you trigger a Refresh() on your DirectoryCatalog. The parts look like this:

C#
[Import(AllowDefault = true, AllowRecomposition=true)]
string test;

dirCatalog.Refresh();

I added the named parameter AllowDefault to my Import as well, just to demonstrate its use. This could also be useful if you are importing a single export and you are going to allow recomposition on that part. If the system doesn't detect a matching export right away, you don't want the system to blow up. By the time you get around to using that variable, the recomposition might have already happened and the part might be populated.

Note that when the parts are recomposed, if a part gets added to an ImportMany, all of the parts in that list get rebuilt. That is by design but it means that you need to understand how recomposition will affect your application and make development decisions based upon that fact. Also, the AppDomain will continue to hold a dll in memory until it is restarted. That means that you will need to use ShadowCopy to delete dlls currently in use. If you do, recomposition will remove these parts from your assembly (but still hold a copy of the dll in memory). Your application will work correctly but it isn't totally clean.

Other Features of MEF

We have touched on the mainline features of MEF, but there are still a lot of areas that we haven't covered. Some of these are only rarely used, but there are at least a few that I feel are more useful. Here are a few that I think would be most useful to you (in no particular order).

Constructor Injection

Example Project Name: Example06

MEF provides us with an option on how we bring in an export. Normally, we will decorate a variable with an [Import] tag. However, there is another option for populating the variable and that is via Constructor Injection. The simplest way to do this is to simply add the [ImportingConstructor] over your specialized constructor like so:

C#
public class MyClass
{
    [ImportingConstructor]
    public MyClass(IRule myRule)
    {
        _myRule = myRule;
    }
}

I created a specialized constructor for my class that took an interface as an argument. I then decorated the constructor with the ImportingConstructor tag to indicate that I wanted MEF to populate this constructor upon composition. MEF will infer which parts it needs just like it would on an [Import] statement with no modifiers. However, should you want to add modifiers to tell MEF what parts you want specifically, you can do so like this:

C#
public class MyClass
{
    [ImportingConstructor]
    public MyClass([Import(typeof(IRule))] IRule myRule)
    {
        _myRule = myRule;
    }
}

This makes the constructor a bit messier but you can do everything you can with an [Import] or [ImportMany] tag in your constructor. So now the question becomes "why?". I believe the best use case for this method is when you want to control what happens when a part is created. For example, you might want to initialize values or otherwise set up your system. For example, if you have a User class that imports a class instance that handles payroll information for the user, you might want to pass in the UserID to the payroll instance right away so it knows who it is working with.

Getting an Export Manually

Example Project Name: Example06

There may be times when you want to get an exported part manually instead of using an import statement. Here are three examples of how to do this:

C#
string manual1 = container.GetExportedValue<string>();
string manual2 = container.GetExportedValue<string>("ContainerName");
string manual3 = container.GetExportedValueOrDefault<string>();

The first example just finds a part of type string. The second finds a part of type string with the specified container name. The third shows how you can do the same thing using the GetExportedValueOrDefault method instead so that the system will not throw an exception if the part is not found.

Personally, I shy away from this way of getting values. It gets ugly quick and it makes you expose your container (in some method - don't just make it a global variable) to a wider audience than you otherwise would need to do.

Getting an Alert when MEF is Done

Example Project Name: Example06

There may be times when you need to know when the import process is complete. This is fairly simple to do. First, you need to implement the interface IPartImportsSatisfiedNotification. This interface provides you with the method OnImportsSatisfied(). This method will be fired off when all of the imports have been successfully hooked up.

Shutting Down MEF Early

Example Project Name: Example06

There may be times when you want to explicitly close out MEF and release the resources before the rest of your application is closed. To do this, call the Dispose() method on your container like so:

C#
container.Dispose();

Different parts will close out differently, but basically the parts held in the container will be properly closed out and disposed of when the container is disposed.

Gotchas

In the course of working with MEF, you will come across errors and problems. Here are a few of the more common issues and how to resolve them:

  1. “No valid exports were found that match the constraint” – This error basically tells you the issue. MEF didn’t find an export to hook into your import. Why this happens can be a bit baffling at times. First, check to see that you are exporting an item of the expected type. Then, make sure the contract name is exactly the same on both ends (if you are using one). Do the same with contract type. Finally, if all else fails and you are sure that everything matches, make sure you are looking in the given assembly for the export. If you are only looking in dll files and forgot to add the current assembly, you may be missing your export.
  2. “More than one export was found that matches the constraint” – When you have an Import statement, you can only have one Export that matches. If MEF finds two, it doesn’t know which one to use so it throws this error. Either use an ImportMany or modify one of the Export statements to have a different signature. This error is especially common when you are letting MEF assume the contract type. If you export two strings, you will get this error. Instead, try to develop everything towards an Interface.
  3. “The export is not assignable to type…” – This means that you specified an Import type but you can’t put that type into the variable. Just because you call an int a string doesn’t make it so. The other possibility here is that you are trying to do an ImportMany but you accidentally put just Import. In that case, you would be trying to put one item into an item of type IEnumerable, which won’t work (it tries to assign it instead of calling the Add method). The reverse is also true - if you put an ImportMany on a variable but the variable cannot accept a set of instances, you will get this error.

For more information on MEF errors, MSDN has some good information here: http://msdn.microsoft.com/en-us/library/ff603380.aspx

Ways to Use MEF

MEF can be (and is) used all over the .NET stack. It is common to see it used with WPF and Silverlight (look into Caliburn.Micro for an excellent implementation of MEF in conjunction with MVVM). You can, however, also use MEF with ASP.NET to make MVC more extensible. Basically, the technology will work with just about anything. It is more a matter of ensuring that the use case justifies the tool.

The Future of MEF

MEF has been around for a while. Currently the team is working on building a new version of MEF (currently referred to as MEF2 - clever, right?). You can find out more about what the latest build includes by reading the BCL Team blog.

Conclusion

In this article, we have started at the very beginning of the Managed Extensibility Framework and worked our way steadily through the major parts of MEF. We have covered the simple examples, but also the more realistic cases that we are likely to see in a production application. I hope that this has been helpful to you. Please let me know below if there is something that you found unclear. As always, I appreciate your constructive feedback.

History

  • May 2, 2012 - Initial Version
  • May 16, 2012 - Added Gotchas section
  • May 19, 2012 - Minor bug fixes 
  • May 21, 2012 - Added the Why Should I Care About MEF section 

License

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


Written By
Software Developer (Senior) DeGarmo
United States United States
I am currently a Senior Software Developer at a company in Illinois called DeGarmo. My primary skills are in .NET, SQL, JavaScript, and other web technologies although I have worked with PowerShell, C, and Java as well.

In my previous positions, I have worked as a lead developer, professor and IT Director. As such, I have been able to develop software on a number of different types of systems and I have learned how to correctly oversee the overall direction of technology for an organization. I've developed applications for everything from machine automation to complete ERP systems.

I enjoy taking hard subjects and making them easy to understand for people unfamiliar with the topic.

Comments and Discussions

 
GeneralRe: My vote of 5 Pin
Tim Corey8-May-12 2:57
professionalTim Corey8-May-12 2:57 
GeneralRe: My vote of 5 Pin
Walby8-May-12 5:31
Walby8-May-12 5:31 
AnswerRe: My vote of 5 Pin
Tim Corey8-May-12 5:44
professionalTim Corey8-May-12 5:44 
GeneralMy vote of 5 Pin
Dharmesh_Kemkar7-May-12 16:26
Dharmesh_Kemkar7-May-12 16:26 
GeneralRe: My vote of 5 Pin
Tim Corey7-May-12 16:29
professionalTim Corey7-May-12 16:29 
GeneralMy vote of 5 Pin
hoernchenmeister7-May-12 4:08
hoernchenmeister7-May-12 4:08 
GeneralRe: My vote of 5 Pin
Tim Corey7-May-12 9:14
professionalTim Corey7-May-12 9:14 
GeneralMy vote of 5 Pin
ring_02-May-12 19:55
ring_02-May-12 19:55 
Just awesome.
GeneralRe: My vote of 5 Pin
Tim Corey7-May-12 9:15
professionalTim Corey7-May-12 9:15 
QuestionMVC integration Pin
Kamyar2-May-12 7:58
Kamyar2-May-12 7:58 
AnswerRe: MVC integration Pin
Tim Corey2-May-12 8:00
professionalTim Corey2-May-12 8:00 
GeneralRe: MVC integration Pin
Kamyar2-May-12 8:01
Kamyar2-May-12 8:01 
QuestionGreat tutorial Pin
Kamyar2-May-12 7:56
Kamyar2-May-12 7:56 
AnswerRe: Great tutorial Pin
Tim Corey2-May-12 8:01
professionalTim Corey2-May-12 8:01 

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.