C# MEF RegistrationBuilder导出特定接口实现
我有一个接口的多个实现,我只想通过编程导出一个。我已经研究了C# MEF RegistrationBuilder导出特定接口实现,c#,mef,composition,C#,Mef,Composition,我有一个接口的多个实现,我只想通过编程导出一个。我已经研究了RegistrationBuilder和它的AddMetaData()函数,但这是为导出定义元数据,而不是为特定值过滤。例如,我想做这样的事情: public enum MyClassType { TypeA, TypeB } public interface IClass {} public interface ClassMetaData { MyClassType Type { get; } } [ExportMetadata("
RegistrationBuilder
和它的AddMetaData()
函数,但这是为导出定义元数据,而不是为特定值过滤。例如,我想做这样的事情:
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.
public enum MyClassType{TypeA,TypeB}
公共接口类{}
公共接口类元数据{MyClassType类型{get;}}
[ExportMetadata(“Type”,MyClassType.TypeA)]
公共MyClassA:IClass
{
公共MyClassType类型{get{return MyClassType.TypeA;}}
}
[ExportMetadata(“类型”,MyClassType.TypeB)]
公共MyClassB:IClass
{
公共MyClassType类型{get{return MyClassType.TypeB;}}
}
//…然后在我的引导课程中,我设置了MEF容器。。。
var registrationBuilder=new registrationBuilder();
registrationBuilder.forTypesDrivesFrom()。。。。
//如何在^中指定一个过滤器,即仅导出带有元数据的实现。Type==MyClassA或instance.Type==MyClassA。
看起来不可能做到。如果您从我的测试代码中注意到,RegistrationBuilder
甚至不会导出用ExportMetadata
属性修饰的类(消息:System.ComponentModel.Composition警告:102:将应用于“test.MyClassA”类型的导出规范约定已被源文件中应用的属性或以前的约定覆盖)。MEF属性优先于通过RegistrationBuilder进行的Fluent配置
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; }
}
+1问这个问题-自从4.5发布以来,我一直没有机会看MEF,所以它迫使我跟上了新添加的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; }
}
}
解决问题的关键是RegistrationBuilder
上的ForTypesMatching()
方法。该参数是一个谓词,它接受该类型并返回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; }
}
}
内部类程序
{
私有静态void Main(字符串[]args)
{
var registrationBuilder=new registrationBuilder();
注册生成器
.ForTypesMatching(t=>FilterOnMetadata(t,MyClassType.TypeOne))
.ExportInterfaces();
var assemblyCatalog=newassemblycatalog(typeof(MyClassType).Assembly,registrationBuilder);
var compositionContainer=新的compositionContainer(assemblyCatalog);
var ic=新的TestImportContainer();
复合容器。复合部件(ic);
var count=ic.ImportedParts.count();
}
公共静态bool过滤器元数据(类型t,MyClassType)
{
变量metadataAttribute=
(MyMetadataAttribute)t.GetCustomAttributes(true)
.SingleOrDefault(at=>at是MyMetadataAttribute);
if(metadataAttribute!=null)
返回metadataAttribute.Type==classType;
返回false;
}
私有密封类TestImportContainer
{
[进口数量(类型(IClass))]
公共IEnumerable导入部件{get;set;}
}
}
是否可以不使用[ExportMetadata]
属性,即只使用一个返回枚举值的class属性(如我的示例,但不使用元数据属性)?否-必须使用标准的[ImportMany]IEnumerable
并在构成对象时从那里进行过滤。更新了另一个示例,如果使用导出
属性,则会对目录进行过滤。这对您有帮助:)但我不同意Lawrence的解决方案,因为它不太粗糙。没问题;更好的解决方案显然是不使用ExportMetadata
,但问题表明您使用了该属性。请注意!非常感谢你。一个问题是,Export和ExportInterface之间的区别是什么?假设您有一个类T
,它实现了IIntefaceOne
和IIntefacetwo
。使用Export()
配置此类将匹配T
类型的导入。使用Export
将该类与类型为IIntefaceOne
的导入相匹配。使用ExportInterfaces()
将匹配类型为IIntefaceOne
和IIntefacetwo
的导入。这个评论可能无法很好地解释它。我发现有一篇很棒的文章非常有用。