MEF Метаданные из экспортируемых частей - PullRequest
4 голосов
/ 21 ноября 2010

Я хочу использовать MEF для системы плагинов для приложения, которое я создаю.Каждый компонент, для которого я хочу иметь идентификатор (GUID), с которым я хочу иметь возможность просматривать.Но этот идентификатор также полезен при работе с экспортированной деталью.

Есть ли способ получить атрибут метаданных, который содержит идентификатор, а также свойство (или метод) экспортируемой детали, если бы разработчики не заполняли его дважды или использовали отражение, чтобы найти его по атрибуту?

Ответы [ 2 ]

7 голосов
/ 21 ноября 2010

Вероятно, это будет смесь атрибута метаданных MEF и абстрактного базового класса. Я бы определил свой плагин-контракт как что-то вроде:

public interface IPluginMetadata
{
  Guid PluginId { get; }
}

public interface IPlugin : IPluginMetadata
{
  void Initialise();
}

Я установил, что интерфейс IPlugin также наследует наш контракт метаданных IPluginMetadata. Далее мы можем создать собственный атрибут экспорта:

[AttributeUsage(AttributeTargets.Class, Inherit = true), MetadataAttribute]
public class ExportPluginAttribute : ExportAttribute, IPluginMetadata
{
  public ExportPluginAttribute(string pluginId) : base(typeof(IPlugin))
  {
    if (string.IsNullOrEmpty(pluginId))
      throw new ArgumentException("'pluginId' is required.", "pluginId");

    PluginId = new Guid(pluginId);
  }

  public Guid PluginId { get; private set; }
}

Вам не нужно украшать атрибут экспорта контрактом метаданных IPluginMetadata, поскольку MEF все равно будет проецировать свойства, но я предпочитаю это делать, поэтому, если я внесу изменения в свой контракт метаданных, тогда мой экспорт атрибут тоже должен быть обновлен. Без вреда, без фола.

Как только мы это сделаем, мы можем определить абстрактный базовый класс для реализации нашего контракта на плагин:

public abstract class PluginBase : IPlugin
{
  protected PluginBase()
  {
    var attr = GetType()
      .GetCustomAttributes(typeof(ExportPluginAttribute), true)
      .Cast<ExportPluginAttribute>()
      .SingleOrDefault();

    PluginId = (attr == null) ? Guid.Empty : attr.PluginId;
  }

  public virtual Guid PluginId { get; private set; }

  public abstract void Initialise();
}

Затем мы можем получить пользовательский атрибут через конструктор абстрактного класса и соответствующим образом применить свойство. Что мы можем сделать:

public IPlugin GetPlugin(Guid id)
{
  var plugin = container
    .GetExports<IPlugin, IPluginMetadata>()
    .Where(p => p.Metadata.PluginId == id)
    .Select(p => p.Value)
    .FirstOrDefault();

  return plugin;
}

А также:

[ExportPlugin("BE112EA1-1AA1-4B92-934A-9EA8B90D622C")]
public class MyPlugin : PluginBase
{
  public override Initialise()
  {
    Console.WriteLine(PluginId);
  }
}

Мы видим, что PluginId предоставляется как через экспортированные метаданные, так и через свойство нашего плагина.

Этот код еще не проверен, но я надеюсь, что он подтолкнет вас в правильном направлении.

0 голосов
/ 22 ноября 2010

Поместите GUID в константу и используйте его как для свойства, так и для метаданных:

[Export(typeof(IFoo))]
[ExportMetadata("GUID", _guid)]
public class Foo : IFoo
{
    private const string _guid = "abc";

    public string Guid { get { return _guid; } }
}

Обратите внимание, что вместо string нельзя использовать тип Guid, поскольку это не разрешено ключевым словом const.

...