2010-11-21 16 views
4

Je cherche à utiliser MEF pour un système de plugin pour une application que je construis. Chaque composant sur lequel je veux avoir un identifiant (un GUID) que je veux pouvoir consulter. Mais cet ID est également utile lors de l'utilisation de la pièce exportée. Y at-il un moyen pour que je puisse avoir un attribut Metadata qui contient l'ID ainsi qu'une propriété (ou méthode) sur la partie exportée, à moins que les développeurs ne la remplissent deux fois ou utilisent la réflexion pour la trouver dans l'attribut ?MEF Métadonnées des pièces exportées

Répondre

7

Il s'agit probablement d'un mélange d'un attribut de métadonnées MEF et d'une classe de base abstraite. Je définirais mon contrat de plugin comme quelque chose comme:

public interface IPluginMetadata 
{ 
    Guid PluginId { get; } 
} 

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

J'ai été mises en application que l'interface IPlugin hérite également notre contrat de métadonnées IPluginMetadata. Ensuite, nous pouvons créer un attribut d'exportation personnalisé:

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

Vous n'avez pas besoin pour décorer l'attribut d'exportation avec le contrat de métadonnées IPluginMetadata, MEF projettera les propriétés de toute façon, mais je préfère le faire, donc si J'introduis des changements à mon contrat de métadonnées, alors mon attribut export devrait être mis à jour aussi. Pas de mal, pas de faute.

Une fois que nous avons fait, nous pouvons définir une classe de base abstraite dont la mise en œuvre de notre contrat de plugin:

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

On peut alors saisir l'attribut sur mesure par le constructeur de la classe abstraite, et appliquer la propriété en conséquence. Que nous pouvons faire:

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

    return plugin; 
} 

Et aussi:

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

Nous pouvons voir que sur PluginId est exposée à la fois par métadonnées exportées, ainsi qu'une propriété de notre plugin.

Ce code n'a pas encore été testé, mais j'espère qu'il vous pousse dans la bonne direction.

+0

+1 très jolie idée et mise en œuvre! –

+1

C'est ce que je pensais que je devrais suivre, bien que je signale que vous ne pouvez pas avoir un Guid comme le type d'une propriété sur l'objet de métadonnées. Pas un accord * énorme *, j'utilise juste un CodeContract pour l'appliquer dans le ctor de l'attribut –

0

Mettez le GUID dans une constante, et de l'utiliser à la fois une propriété et les métadonnées:

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

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

Notez que vous ne pouvez pas utiliser le type Guid au lieu de string, car cela est interdit par la const mot-clé.