如何使用 MEF 继承导出和导出元数据?

发布于 2024-11-18 17:55:17 字数 1324 浏览 4 评论 0原文

我有一个接口:

[InheritedExport(typeof(IMetric))]
public interface IMetric { ... }

我有一个元属性接口:

 public interface IMetricAttribute { ... }

以及一个实现它的属性:

[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class MetricAttribute : ExportAttribute, IMetricAttribute {
    public string MetricName { get; set; }
    public string MetricDescription { get; set; }

    public MetricAttribute(string name, string description)
        : base(typeof(MetricAttribute)) {
        this.MetricName = name;
        this.MetricDescription = description;
    }
}

然后我有两个类:

[Metric("MetricA","MetricA")]
public class MetricA: IMetric { ... }

[Export(typeof(IMetric))] <<<< THIS IS IMPORTANT
[Metric("MetricB", "MetricB")]
public class MetricB: IMetric { ... }

然后我尝试导入指标(我可以在目录中看到两者)

以下返回是 MetricA 和 MetricB

var metrics = compositionContainer.GetExports<IMetric>();

但是以下仅返回 MetricB 而不是 MetricA

var metrics = compositionContainer.GetExports<IMetric, IMetricAttribute>();

知道为什么吗?

(注意 MetricB 上的重复导出(它已经通过实现 IMetric 获得了它))

谢谢

David

I have an interface:

[InheritedExport(typeof(IMetric))]
public interface IMetric { ... }

I have a Meta attribute interface:

 public interface IMetricAttribute { ... }

and an attribute that implements it:

[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class MetricAttribute : ExportAttribute, IMetricAttribute {
    public string MetricName { get; set; }
    public string MetricDescription { get; set; }

    public MetricAttribute(string name, string description)
        : base(typeof(MetricAttribute)) {
        this.MetricName = name;
        this.MetricDescription = description;
    }
}

I then have two classes:

[Metric("MetricA","MetricA")]
public class MetricA: IMetric { ... }

[Export(typeof(IMetric))] <<<< THIS IS IMPORTANT
[Metric("MetricB", "MetricB")]
public class MetricB: IMetric { ... }

I then try to import the metrics ( i can see both in the cataloge)

The following returns be MetricA AND MetricB

var metrics = compositionContainer.GetExports<IMetric>();

However the following returns ONLY MetricB and NOT MetricA

var metrics = compositionContainer.GetExports<IMetric, IMetricAttribute>();

any idea why?

(note the duplicate export on MetricB (it already has it from implementing IMetric))

thanks

David

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

傾城如夢未必闌珊 2024-11-25 17:55:17

我第一次看到这种行为,但据我所知,元数据是在类型级别每次导出时生成的。因此,假设:

[Metric("MetricA", "MetricA")]
public class MetricA : IMetric
{

}

您有两个该类型的导出。您拥有由 MetricAttribute 隐式提供的 MetricA 导出,并且拥有由 InheritedExport 提供的 IMetric 继承导出界面上的 (typeof(IMetric)) 属性。

如果您查看容器,您会注意到为 MetricA 定义的两个导出。这是第一个,及其元数据:

在此处输入图像描述

这是第二个:

在此处输入图像描述

您会注意到元数据是在 MetricA 的导出上完成的,而不是在继承的导出上完成的。如果我添加了进一步的导出,例如将 [Export("test")] 添加到 MetricA,您将获得另一个导出定义,其中 MetricName 具有相同的元数据项名为“test”的合约的MetricDescription。这表明,在分析类型时,会识别导出属性,并且创建的导出定义包括在抽象树中同一级别指定的元数据。

执行您想要的操作的最简单方法是删除 InheritedExport,并将 MetricAttribute 的定义修改为:

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false), MetadataAttribute]
public class MetricAttribute : ExportAttribute, IMetricAttribute
{
    public MetricAttribute(string name, string description)
        : base(typeof(IMetric))
    {
        this.MetricName = name;
        this.MetricDescription = description;
    }

    public string MetricName { get; private set; }
    public string MetricDescription { get; private set; }

}

然后传入 typeof(IMetric ) 到基本 ExportAttribute 构造函数。然后,您可以正确获取 GetExports()GetExports() 的两个导出。

First time I've seen this behaviour, but from what I can understand, metadata is generated per-export at the type level. So, given:

[Metric("MetricA", "MetricA")]
public class MetricA : IMetric
{

}

You have two exports for this type. You have the export of MetricA which is implictly provided by your MetricAttribute, and you have the inherited export for IMetric provided by the InheritedExport(typeof(IMetric)) attribute on your interface.

If you look at the container, you'll notice two exports defined for MetricA. Here is the first, with its metadata:

enter image description here

And here is the second:

enter image description here

You'll notice that the metadata is done on the export of MetricA, not the inherited export. If I added a further export, lets say [Export("test")] to MetricA, you get another export definition, with the same metadata items for MetricName and MetricDescription for the contract named "test". This shows you that as the type is analysed, the export attribute is identified, and the export definition that is created includes the metadata specified at the same level in the abstraction tree.

The easiest way to do what you want, is to drop out the InheritedExport, and modify your definition of your MetricAttribute to:

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false), MetadataAttribute]
public class MetricAttribute : ExportAttribute, IMetricAttribute
{
    public MetricAttribute(string name, string description)
        : base(typeof(IMetric))
    {
        this.MetricName = name;
        this.MetricDescription = description;
    }

    public string MetricName { get; private set; }
    public string MetricDescription { get; private set; }

}

Where you then pass in typeof(IMetric) to the base ExportAttribute constructor. You then correctly get the two exports for GetExports<IMetric>() and GetExports<IMetric, IMetricAttribute>().

携君以终年 2024-11-25 17:55:17

我遇到了同样的问题,并找到了一个对我来说效果很好的不同解决方案:
我刚刚向界面添加了元数据!

[InheritedExport(typeof(IMetric))]
[Metric("name","description")]
public interface IMetric { ... }

您可以将字段留空或使用 null 作为默认值,但在此处指定元数据非常重要。
然后指定不带导出属性的类:

[Metric("MetricA")]
public class MetricA: IMetric { ... }

请注意,您可以只指定一个元数据,但在这种情况下,第二个元数据不会是 description,而是 null!所以界面中的元数据不是默认值。
总而言之,这对我有用,我可以将 InheritedExport 与我的元数据一起使用:-)

I came across the same problem and found a differen solution which worked fine for me:
I just added metadata to the interface!

[InheritedExport(typeof(IMetric))]
[Metric("name","description")]
public interface IMetric { ... }

You can leave the fields blank or use null as default, but it is important to specify the Metadata here.
Then you specify your classes without export attribute:

[Metric("MetricA")]
public class MetricA: IMetric { ... }

Be aware that you can can specify just one metadata, but the second one won't be description in this case, it will be null! So the metadata in the interface are NOT default values.
All in all this worked for me and I can use InheritedExport with my metadata :-)

陌路黄昏 2024-11-25 17:55:17

为了澄清 Matthew 的答案:

当您定义自定义元数据属性类 MetricAttribute 并继承 ExportAttribute 时,您实质上是将 [Export] 属性添加到您使用 [Metric] 属性修饰的所有类。这意味着您不再需要界面上的 [InheritedExport] 属性,因为它只是创建单独的导出定义,而无需任何元数据。

如果您想创建更可重用的元数据属性,您可以在 MetricAttribute 中公开 ExportAttribute 构造函数参数,如下所示:

public MetricAttribute(Type contractType, string name, string description)
    : base(contractType)
{
    this.MetricName = name;
    this.MetricDescription = description;
}

通过引入 contractType 变量,您可以现在用以下内容补充您的定义

[Export(typeof(IMetric))]
[Metric("MetricB", "MetricB")]
public class MetricB: IMetric { ... }

[Metric(typeof(IMetric), "MetricB", "MetricB")]
public class MetricB: IMetric { ... }

To clarify on Matthew answer:

When you are defining custom metadata attribute class MetricAttribute and inheriting from ExportAttribute, you are essentially adding [Export] attribute to all classes that you decorate with your [Metric] attribute. This means that you don't need [InheritedExport] attribute on the interface anymore as it just creates separate export definition without any metadata.

If you want to create a more reusable metadata attribute, you can expose ExportAttribute constructor parameters in your MetricAttribute as follows:

public MetricAttribute(Type contractType, string name, string description)
    : base(contractType)
{
    this.MetricName = name;
    this.MetricDescription = description;
}

By introducing contractType variable you can now supplement your definition of

[Export(typeof(IMetric))]
[Metric("MetricB", "MetricB")]
public class MetricB: IMetric { ... }

with:

[Metric(typeof(IMetric), "MetricB", "MetricB")]
public class MetricB: IMetric { ... }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文