现在的位置: 首页 > 综合 > 正文

MEF框架学习之旅(六)元数据和元数据视图

2013年08月13日 ⁄ 综合 ⁄ 共 3614字 ⁄ 字号 评论关闭

为了允许访问元数据,MEF 使用 .NET Framework 4 的一个新 API,即 System.Lazy<T>。使用该 API 可延迟实例的实例化,直至访问 Lazy 的 Value 属性。MEF 使用 Lazy<T,TMetadata> 进一步扩展 Lazy<T>,以允许在不实例化基础导出的情况下访问导出元数据。

元数据可用于将导出的对象的属性传递到导入部件。 导入部件可以使用此数据来决定要使用哪些导出,或收集有关导出的信息而不必构造导出。 因此,导入必须为延迟导入才能使用元数据。

为了使用元数据,您通常会声明一个称为“元数据视图”的接口,该接口声明什么元数据将可用。 元数据视图接口必须只有属性,并且这些属性必须具有 get 访问器。访问元数据属性时,MEF 将动态实现 TMetadata,且将基于导出提供的元数据来设置值。

注意:

1.如果接口/继承类中有字段或属性,则在导出具体实例时需要为每一个属性或字段赋值;或者将DefaultValue特性标注在需要的字段或属性上。

2.接口中的属性只能get不能set

下面的接口是一个示例元数据视图。

代码段

public interface IPluginMetadata
{
    string Name { get; }

    [DefaultValue(1)] 
    int Version { get; }
}

 

也可以使用泛型集合 IDictionary<string, object> 作为元数据视图,但这样将会丧失类型检查的优点,因此应避免这样做。

通常,在元数据视图中命名的所有属性都是必需的,并且不会将未提供这些属性的任何导出视为匹配。 DefaultValue 特性指定属性是可选的。 如果未包括属性,则将为其分配指定为 DefaultValue 的参数的默认值。 下面是用元数据修饰的两个不同的类。 这两个类都将与前面的元数据视图匹配。

代码段

 

[Export(typeof(IPlugin)),
ExportMetadata("Name", "Logger"),
ExportMetadata("Version", 4)]
public class Logger : IPlugin
{

}
 
[Export(typeof(IPlugin)),
ExportMetadata("Name", "Disk Writer")]
//Version is not required because of the DefaultValue
public class DWriter : IPlugin
{

}

 

元数据是通过使用 ExportMetadata 特性在 Export 特性之后表示的。 每一段元数据都由一个名称/值对组成。 元数据的名称部分必须与元数据视图中相应属性的名称匹配,并且值将分配给该属性。

导入程序负责指定将使用的元数据视图(如果有)。 包含元数据的导入将声明为延迟导入,其元数据接口作为 Lazy<T,T> 的第二个类型参数。 下面的类导入前面的部件以及元数据。

代码段

public class Addin
{

    [Import]
    public Lazy<IPlugin, IPluginMetadata> plugin;

}

 

在许多情况下,您需要将元数据与 ImportMany 特性结合,以便分析各个可用的导入并选择仅实例化一个导入,或者筛选集合以匹配特定条件。 下面的类仅实例化具有 Name 值“Logger”的 IPlugin 对象。

代码段

public class User
{

    [ImportMany]
    public IEnumerable<Lazy<IPlugin, IPluginMetadata>> plugins;
 
    public IPlugin InstantiateLogger ()
    {

        IPlugin logger = null;
 
        foreach (Lazy<IPlugin, IPluginMetadata> plugin in plugins)
        {
            if (plugin.Metadata.Name = "Logger") logger = plugin.Value;

        }
        return logger;

    }

}

 

使用 ExportMetadata 特性可提供很大的灵活性,但是使用该特性时需要注意一些事项:

在 IDE 中无法发现元数据键。部件编写者必须知道对导出有效的元数据键和类型。

编译器不会验证元数据以确保其正确。

ExportMetadata 会向代码添加更多干扰信息,从而隐藏真正意图。

MEF 提供了解决方案来解决以上的问题:自定义导出。

MEF 允许创建包括其自己的元数据的自定义导出。创建自定义导出包括创建还指定元数据的派生 ExportAttribute。

下面的类定义一个自定义特性。

代码段

[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple=true)]
public class MyAttribute : ExportAttribute
{

    public MyAttribute(string myMetadata)
        : base(typeof(IMyAddin))
    {

        MyMetadata = myMetadata;

    }

     public string MyMetadata { get; private set; }

}

 

此类定义一个名为 MyAttribute 的自定义特性,具有协定类型 IMyData 和某些名为 MyMetadata 的元数据。 用 MetadataAttribute 特性标记的类中的所有属性都将被视为自定义特性中定义的元数据。 下面两个声明等效。

代码段

 

[Export(typeof(IMyAddin),
ExportMetadata("MyMetadata", "theData")]
public MyAddin myAddin { get; set; }
 
[MyAttribute("theData")]
public MyAddin myAddin { get; set; }

 

在第一个声明中,协定类型和元数据是显式定义的。 在第二个声明中,协定类型和元数据在自定义特性中是隐式的。 特别是,在必须将大量的相同元数据(例如,作者或版权信息)应用于多个部件的情况下,使用自定义特性可以节约大量的时间和重复工作。 此外,可以创建自定义特性的继承树来为变体留出余地。

若要在自定义特性中创建可选元数据,您可以使用 DefaultValue 特性。 如果此特性应用于自定义特性类中的属性,它将指定修饰的属性是可选的,并且不必由导出程序提供。 如果未提供属性的值,则将为属性分配其属性类型的默认值(通常为 nullfalse 或 0。)

再来看一个示例:

代码段

public interface IClassMetadata
    {

        string ClassName { get; }
        string OtherInfo { get; }
    }

 

    [MetadataAttribute]
    [AttributeUsage(AttributeTargets.Class, AllowMultiple=false)]
    public class ExportStudent : ExportAttribute, IClassMetadata
    {

        public ExportStudent()
            : base() { }

        public ExportStudent(string contractName)
            : base(contractName) { }

        public ExportStudent(Type contractType)
            : base(contractType) { }

        public ExportStudent(string contractName, Type contractType)
            : base(contractName, contractType) { }
 
        public string ClassName { get; set; }

 

        //可选时必须加上

        [DefaultValue("")]
        public string OtherInfo { get; set; }

     }

 

    [ExportStudent(ClassName = "一年级三班)]
    public class Student
    {

        public string Name { get; set; }
        public int Age { get; set; }

    }

 

ExportStudent使用 MetadataAttribute 进行修饰,这指定该特性提供元数据。此特性告知 MEF 查看所有公共属性,并通过将属性名称用作键,对导出创建相关联的元数据。在这种情况下,唯一的元数据为ClassName、OtherInfo。在这个实例中AttributeUsage属性仅对类有效,且只能存在一个 ExportStudent特性,一般来说,AllowMultiple 应设置为 false;如果为 true,则导入程序将传递一组值而不是单个值。当多个导出具有同一成员的同一约定的不同元数据时,AllowMultiple 应保留为 True。

如您所见,自定义导出可确保为特定导出提供正确的元数据。这些导出还可减少代码中的干扰信息,更加容易发现,并且可通过特定于域来更好地表达意图。

抱歉!评论已关闭.