我有一个接口的多个实现,我想以编程方式只导出一个。我查看了RegistrationBuilder
和它的AddMetaData()
函数,但这是为导出定义MetaData,而不是过滤特定值。例如,我想做这样的事情:
public enum MyClassType { TypeA, TypeB }
public interface IClass {}
public interface ClassMetaData { MyClassType Type { get; } }
[ExportMetadata("Type", MyClassType.TypeA)]
public MyClassA : IClass
{
public MyClassType Type { get { return MyClassType.TypeA; } }
}
[ExportMetadata("Type", MyClassType.TypeB)]
public MyClassB : IClass
{
public MyClassType Type { get { return MyClassType.TypeB; } }
}
//...Then in my bootstrapping class where I set up the MEF container...
var registrationBuilder = new RegistrationBuilder();
registrationBuilder.ForTypesDerivesFrom<IClass>()....
// How do I specify a filter in ^ to say only export the implementation with MetaData.Type == MyClassA or instance.Type == MyClassA.
答案 0 :(得分:3)
+1的问题 - 我没有机会看到MEF,因为4.5出来所以它迫使我加快了新增RegistrationBuilder
课程的速度!
我猜你的例子不起作用的原因是因为我理解它,RegistrationBuilder
旨在取代MEF依赖于.NET 4.0的属性的角色。 ExportMetadataAttribute
是旧的做事方式的一部分,而我只是猜测旧的和新的不能很好地融合在一起。
由于添加了RegistrationBuilder
,您可以完全按照自己的意愿实现,而不会让导出的类知道它们是使用MEF构建的。在我看来,这是MEF从4.0的巨大改进。
首先让我们从我们要导出的类开始。首先,我定义了MyMetadataAttribute
类,它封装了与我们想要过滤的类型相关联的元数据:
public enum MyClassType
{
TypeOne,
TypeTwo
}
[AttributeUsage(AttributeTargets.Class)]
public class MyMetadataAttribute: Attribute
{
public MyMetadataAttribute(MyClassType type)
{
Type = type;
}
public MyClassType Type { get; private set; }
}
现在来我可能想要导出的类:
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; }
}
}
解决问题的关键是ForTypesMatching()
上的RegistrationBuilder
方法。参数是一个谓词,它接受类型并返回true或false,具体取决于您是否要在导出的结果中包含该类型。下面的代码演示了一个示例:
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; }
}
}
答案 1 :(得分:1)
看起来不像是可以做到的。如果您从我的测试代码中注意到,RegistrationBuilder
甚至不会导出使用ExportMetadata
属性修饰的类(消息:System.ComponentModel.Composition警告:102:导出规范约定,适用于类型'test.MyClassA'已被源文件中应用的属性或先前约定覆盖。 MEF属性优先于RegistrationBuilder
class Program
{
static void Main()
{
// importing class instance
var mcc = new MyClassConsumer();
// type to export
var typeToExport = typeof(MyClassA);
Console.WriteLine("Type to export: {0}", typeToExport);
var rb = new RegistrationBuilder();
rb.ForType(typeToExport)
.ExportInterfaces();
var catalog = new AssemblyCatalog(typeof(Program).Assembly, rb);
var container = new CompositionContainer(catalog);
container.ComposeParts(mcc); // bombs if MyClassA's MetadataAttribute is not commented out
Console.WriteLine("Imported property's type: {0}", mcc.MyClass.GetType());
Console.ReadLine();
}
}
public enum MyClassType { TypeA, TypeB }
public interface IClass {}
public interface IClassMetaData { MyClassType Type { get; } }
[ExportMetadata("Type", MyClassType.TypeA)] // works if you comment this line
public class MyClassA : IClass
{
}
[ExportMetadata("Type", MyClassType.TypeB)]
public class MyClassB : IClass
{
}
public class MyClassConsumer
{
[Import]
public IClass MyClass { get; set; }
}
更新:如果您可以将[Export(IClass)]
添加到要导出的类中,则可以按如下所示过滤目录:
class Program
{
static void Main()
{
var catalog = new AssemblyCatalog(typeof(Program).Assembly);
var filteredCatalog = catalog.Filter(p =>
{
var type = ReflectionModelServices.GetPartType(p).Value;
return typeof(IClass).IsAssignableFrom( type ) && // implements interface you're looking for
Attribute.IsDefined(type, typeof(ExportMetadataAttribute)) && // has ExportMetadata attribute
// check for Type == MyClassType.TypeA
type.GetCustomAttributes(typeof(ExportMetadataAttribute), true).Any(ca =>
{
var ema = (ExportMetadataAttribute)ca;
return ema.Name == "Type" && (MyClassType)ema.Value == MyClassType.TypeA;
});
});
var container = new CompositionContainer(filteredCatalog);
MyClassConsumer mcc = new MyClassConsumer();
container.ComposeParts(mcc);
Console.WriteLine("Imported property's type: {0}", mcc.MyClass.GetType());
Console.ReadLine();
}
}
public enum MyClassType { TypeA, TypeB }
public interface IClass {}
public interface IClassMetaData { MyClassType Type { get; } }
[Export(typeof(IClass))]
[ExportMetadata("Type", MyClassType.TypeA)]
public class MyClassA : IClass
{
}
[Export(typeof(IClass))]
[ExportMetadata("Type", MyClassType.TypeB)]
public class MyClassB : IClass
{
}
public class MyClassConsumer
{
[Import]
public IClass MyClass { get; set; }
}