Как использовать MEF Inherited Export & MetaData? - PullRequest
15 голосов
/ 04 июля 2011

У меня есть интерфейс:

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

Iзатем есть два класса:

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

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

Затем я пытаюсь импортировать метрики (я могу видеть оба в каталоге)

Следующие возвращаемые значения будут MetricA AND MetricB

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

Однако следующее возвращает ТОЛЬКО MetricB, а НЕ MetricA

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

любая идея почему?

(обратите внимание на дублирующий экспорт в MetricB (он уже имеет его отIMetric))

спасибо

Дэвид

Ответы [ 3 ]

14 голосов
/ 06 июля 2011

Впервые я увидел такое поведение, но насколько я понимаю, метаданные генерируются для каждого экспорта на уровне типа. Итак, учитывая:

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

}

У вас есть два экспорта для этого типа. У вас есть экспорт MetricA, который косвенно предоставляется вашим MetricAttribute, и у вас есть унаследованный экспорт для IMetric, предоставленный атрибутом InheritedExport(typeof(IMetric)) в вашем интерфейсе.

Если вы посмотрите на контейнер, вы увидите два экспорта, определенных для MetricA. Вот первое, с его метаданными:

enter image description here

А вот и второе:

enter image description here

Вы заметите, что метаданные создаются при экспорте MetricA, а не унаследованном экспорте. Если я добавил дополнительный экспорт, скажем, от [Export("test")] до MetricA, вы получите другое определение экспорта с теми же элементами метаданных для MetricName и MetricDescription для контракта с именем «test». Это показывает, что при анализе типа атрибут экспорта идентифицируется, а созданное определение экспорта включает метаданные, указанные на том же уровне в дереве абстракции.

Самый простой способ сделать то, что вы хотите, это исключить 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<IMetric>() и GetExports<IMetric, IMetricAttribute>().

3 голосов
/ 17 февраля 2013

Я столкнулся с той же проблемой и нашел другое решение, которое отлично сработало: Я только что добавил метаданные в интерфейс!

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

Вы можете оставить поля пустыми или использовать null по умолчанию, но важно указать здесь метаданные. Затем вы указываете свои классы без атрибута экспорта:

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

Помните, что вы можете указать только одну метаданную, но вторая не будет description, в этом случае это будет null! Таким образом, метаданные в интерфейсе НЕ являются значениями по умолчанию. В целом, это сработало для меня, и я могу использовать InheritedExport с моими метаданными: -)

2 голосов
/ 11 июня 2015

Чтобы уточнить ответ Мэтью:

Когда вы определяете класс пользовательских атрибутов метаданных MetricAttribute и наследуете от ExportAttribute, вы по существу добавляете атрибут [Export] ко всем классам, которые вы украшаете с помощью [Metric] атрибут.Это означает, что вам больше не нужен атрибут [InheritedExport] в интерфейсе, поскольку он просто создает отдельное определение экспорта без метаданных.

Если вы хотите создать атрибут метаданных более многократного использования, вы можете выставить ExportAttributeПараметры конструктора в вашем MetricAttribute выглядят следующим образом:

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 { ... }
...