2013-10-30 31 views
2

我有一个接口的多个实现,我想以编程方式导出一个。我已经看过RegistrationBuilder和它的AddMetaData()函数,但这是为导出定义元数据,而不是筛选特定的值。例如,我想要做这样的事情:MEF RegistrationBuilder导出特定的接口实现

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. 

回答

2

+ 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; } 
    } 
} 
+0

现货!非常感谢你。一个问题想到,Export和ExportInterface有什么区别? – clicky

+2

假设你有一个'T'类来实现'IIntefaceOne'和'IInterfaceTwo'。用'Export()'配置这个类将匹配'T'类型的输入。使用'Export '将匹配那个类和'IIntefaceOne'类型的输入。使用'ExportInterfaces()'将匹配'IIntefaceOne'和'IInterfaceTwo'类型的导入。这个评论可能没有很好的解释它。有一篇很棒的文章[这里](http://msdn.microsoft.com/en-us/magazine/jj133818.aspx),我发现它非常有用。 – Lawrence

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; } 
} 

UPDATE接管流利的配置优先级:如果你可以添加[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; } 
} 
+0

可它没有做到的'[ExportMetadata]'属性,即刚刚有一个返回枚举值(如我的例子,但没有元数据属性)一类的属性? – clicky

+0

Nope - 编写对象时,必须使用标准的[ImportMany] IEnumerable >'并从那里过滤。 – Moho

+0

用另一个例子进行了更新,如果使用'Export'属性 – Moho