+1 for the question - I hadn't had a chance to look at MEF since 4.5 came out so it forced me to get up to speed with the newly added RegistrationBuilder
class!
I'm guessing the reason your example doesn't work is because as I understand it, the RegistrationBuilder
is designed to replace the role of attributes that MEF relies on so heavily up to .NET 4.0. The ExportMetadataAttribute
is part of the old way of doing things, and it's just my guess that the old and new don't play well together.
Thanks to the addition of RegistrationBuilder
you can achieve exactly what you want without the exported classes having any knowledge that they are being constructed using MEF. In my opinion this is a vast improvement in MEF from 4.0.
First let us start with the classes we want to export. First of all I define the MyMetadataAttribute
class, which encapsulates the metadata associated to the type we will want to filter on:
public enum MyClassType
{
TypeOne,
TypeTwo
}
[AttributeUsage(AttributeTargets.Class)]
public class MyMetadataAttribute: Attribute
{
public MyMetadataAttribute(MyClassType type)
{
Type = type;
}
public MyClassType Type { get; private set; }
}
Now come the classes that I potentially want to export:
public interface IClass
{
}
[MyMetadata(MyClassType.TypeOne)]
public class MyClassA : IClass
{
public MyClassType Type
{
get { return MyClassType.TypeOne; }
}
}
[MyMetadata(MyClassType.TypeTwo)]
public class MyClassB : IClass
{
public MyClassType Type
{
get { return MyClassType.TypeTwo; }
}
}
The key to solving your problem is the ForTypesMatching()
method on RegistrationBuilder
. The argument is a predicate that takes the type and returns true or false depending on whether you want to include the type in the exported results. The code below demonstrates an example of this:
internal class Program
{
private static void Main(string[] args)
{
var registrationBuilder = new RegistrationBuilder();
registrationBuilder
.ForTypesMatching<IClass>(t => FilterOnMetadata(t, MyClassType.TypeOne))
.ExportInterfaces();
var assemblyCatalog = new AssemblyCatalog(typeof (MyClassType).Assembly, registrationBuilder);
var compositionContainer = new CompositionContainer(assemblyCatalog);
var ic = new TestImportContainer();
compositionContainer.ComposeParts(ic);
var count = ic.ImportedParts.Count();
}
public static bool FilterOnMetadata(Type t, MyClassType classType)
{
var metadataAttribute =
(MyMetadataAttribute) t.GetCustomAttributes(true)
.SingleOrDefault(at => at is MyMetadataAttribute);
if (metadataAttribute != null)
return metadataAttribute.Type == classType;
return false;
}
private sealed class TestImportContainer
{
[ImportMany(typeof(IClass))]
public IEnumerable<IClass> ImportedParts { get; set; }
}
}