Вероятно, это будет смесь атрибута метаданных 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
предоставляется как через экспортированные метаданные, так и через свойство нашего плагина.
Этот код еще не проверен, но я надеюсь, что он подтолкнет вас в правильном направлении.