2010-11-21 13 views
4

Ich suche MEF für ein Plugin-System für eine Anwendung, die ich erstelle. Jede Komponente, auf der ich eine Kennung haben möchte (eine GUID), gegen die ich suchen möchte. Aber diese ID ist auch nützlich, wenn Sie mit dem exportierten Teil arbeiten.MEF Metadaten aus den exportierten Teilen

Gibt es eine Möglichkeit, dass ich ein Metadata-Attribut haben kann, das die ID sowie eine Eigenschaft (oder Methode) für den exportierten Teil enthält, ohne Entwickler zweimal auszufüllen oder mithilfe von Reflektion das Attribut zu finden ?

Antwort

7

Es ist wahrscheinlich eine Mischung aus einem MEF-Metadatenattribut und einer abstrakten Basisklasse.

public interface IPluginMetadata 
{ 
    Guid PluginId { get; } 
} 

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

ich durchgesetzt habe, dass die IPlugin Schnittstelle auch unseren Metadaten Vertrag IPluginMetadata erben: Ich würde meinen Plugin Vertrag als so etwas wie definieren. Als nächstes können wir eine eigene Export-Attribut erstellen:

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

Sie brauchen nicht um den Export-Attribut mit dem Metadaten-Vertrag IPluginMetadata zu dekorieren, als MEF ohnehin die Eigenschaften projizieren wird, aber ich ziehe es so zu tun, also wenn Ich führe Änderungen in meinen Metadatenvertrag ein, dann sollte auch mein Exportattribut aktualisiert werden. Kein Schaden, kein Foul.

Sobald wir dies getan haben, können wir eine abstrakte Basisklasse, von der Definition unseres Plugin Vertrag zu implementieren:

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(); 
} 

Wir können dann die Sitte durch die abstrakte Klasse Konstruktor Attribut greifen, und die Eigenschaft gelten entsprechend. Dass wir tun können:

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

    return plugin; 
} 

Und auch:

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

Wir können sehen, dass aus PluginId ausgesetzt wird sowohl durch exportierten Metadaten sowie eine Eigenschaft unserer Plug-ins.

Dieser Code ist alles noch nicht getestet, aber ich hoffe, es bringt Sie in die richtige Richtung.

+0

+1 hübsche nette Idee und Implementierung! –

+1

Das ist, was ich dachte, ich müsste gehen, obwohl ich darauf hinweisen werde, dass Sie keine Guid als den Typ einer Eigenschaft auf dem Metadatenobjekt haben können. Kein * riesiger * Deal, ich benutze nur einen CodeContract, um es im ctor des Attributs zu erzwingen –

0

Setzen Sie die GUID in einer konstanten, und verwenden Sie es für beide eine Eigenschaft und die Metadaten:

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

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

Beachten Sie, dass Sie nicht den Guid Typ statt string verwenden können, so dass nicht durch die erlaubt ist const Schlüsselwort.