Many months ago I created a post on how to get started with MEF. Ever since then I have been meaning to get this post (and others) about about how to get up and running quickly with MEF.
In this post will demonstrate how to provide Metadata (custom values which can help provide context) information to your Exported item. Why should you care about Metedata? Below is a great reason from the MEF site on codeplex
Declaring Exports explained the basics of parts exporting services and values. In some cases it’s necessary to associate information with exports for a variety of reasons. Commonly it’s used to explain about the capabilities of an specific implementation of a common contract. This is useful to allow imports to either constraint the export that can satisfy it, or to import all available implementations at the time and check their capabilities in runtime before using the export.
Before we get rolling:
What I am going to show in this post is how to create a custom Export Attribute. It is possible to expose Meta via the build in ExportAttribute, but the built in mechanism has a few short comings:
- Not discoverable
- Not strongly typed
- Compiler cannot validate the data content
Lets get rolling:
Pre-Step 1: Taking a look at the Class which we will mark as a Export
public interface IPlugin
{
string PluginAction();
}
[Export(typeof(IPlugin))]
public class DefaultPlugin : IPlugin
{
public string PluginAction()
{
return "This is the default plugin";
}
}
Step 1: Defining the interface which will represent your
In order to create discoverable Metadata you will first want to create an interface which will represent your metadata. Below is my interface.
public interface IPluginMetaData
{
string Name { get; }
string Version { get; }
}
We will later use this interface when we define and use our custom Export Attribute. The main reason for creating this attribute is now the compiler has a hard wired way to know exactly what type of data is being exposed in your Metadaa
Step 2: Defining your custom ExportAttribute Attribute
Now that we created our interface to give us strong typing and discoverability we need to create a custom attribute which will allows the MEF engine to know we have Metadata to expose
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class PluginMetadataAttribute : ExportAttribute
{
public PluginMetadataAttribute( string name, string version)
: base(typeof(IPluginMetaData))
{
Name = name;
Version = version;
}
public string Name { get; set; }
public string Version { get; set; }
}
When looking at the code above there are 3 things to pay close attention to
- You need to make a call into the base constructor of the ExportAttribute and provide it the type of your interface which defines your Metadata
- You need to mark your attribute with the [MetadataAttribute] (which is part of MEF)
- You need to mark the usage type of your attribute. I am not 100% why this is needed (Glenn, you out there with an answer???)
Step 3: Providing your Metdata to your Exported Class
[Export(typeof(IPlugin))]
[PluginMetadata("Default", "1.0.0.0")]
public class DefaultPlugin : IPlugin
{
public string PluginAction()
{
return "This is the default plugin";
}
}
The code above is the same as the code we saw in pre-step 1, but this time we have marked the class with our custom Metadata attribute. We have now officially provided our Metadata to our Export…. But how do we get this information out of MEF?
Step 4: Setting up your [ImportMany] to use the Metadata
If you are using an ImportMany (this should also work on an Import, but I have never tried) most likely you are pushing your Exports into a IList or IEnumerable. If this is the case, your changes are pretty minor. Below is what you need to do in order to have your Metadata loaded.
[ImportMany(typeof(IPlugin), AllowRecomposition = true)]
private IList<Lazy<IPlugin, IPluginMetaData>> _loadedIPlugins = new List<Lazy<IPlugin, IPluginMetaData>>();
If you have not seen the Lazy keyword, you are not alone. This is new to .Net 4 and this provides Lazy initialization support to the framework. (part of the System namespace). One key thing to understand here though is that the Lazy keyword out the box is only Lazy<T>, but the Lazy we are using is Lazy<T, M>. This extended version of Lazy lives in the MEF assemblies (System.ComponentModel.Composition) and will need to be referenced to use.
Step 5: Using the Metadata
Once you have the Export loaded into your container (which by the way does not need to change to have this work) you can access your container by selecting the .Metadata property on your instance of the export. The image below shows it in action.
As you can see, exposing and using Metadata via MEF is a snap.