2 votes

Approche MEF sans attribut : initialisation paresseuse par condition

Mon post précédent contient une tentative d'utiliser une approche sans attribut (basée sur des conventions) pour configurer MEF : MEF 2 : importer beaucoup .
Mais il contient l'utilisation des attributs de métadonnées d'exportation dans la classe PluginMetadataAttribute nécessaire pour l'initialisation paresseuse du plugin par condition (nom spécifique, version).
Comment se débarrasser de la dépendance ExportAttribute ?

3voto

anpv Points 169

J'ai trouvé trois solutions.
Solution 1 (utilisation de champs constants de classe, mauvaise solution) :

public class Plugin1 : IPlugin
{
    public const string Name = "Plugin1";
    public const string Version = "1.0.0.0";

    public void Run()
    {
        Console.WriteLine("Plugin1 runed");
    }
}
// ...
var builder = new RegistrationBuilder();
builder
    .ForTypesDerivedFrom<IPlugin>()
    .Export<IPlugin>(exportBuilder => {
        exportBuilder.AddMetadata("Name", t => t.GetField("Name").GetRawConstantValue());
        exportBuilder.AddMetadata("Version", t => t.GetField("Version").GetRawConstantValue());
    });

Solution 2 (utilisation des propriétés de la classe, mauvaise solution) :

public interface IPluginMetadata
{
    string Name { get; }
    string Version { get; }
}

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

public class Plugin1 : IPlugin
{
    public string Name { get { return "Plugin 1"; } }
    public string Version { get { return "1.0.0.0"; } }

    public void Run()
    {
        Console.WriteLine("Plugin1 runed");
    }
}

Et obtenir les valeurs des propriétés par la méthode décrite ici : https://stackoverflow.com/a/11162876/1986524

Solution 3 (utilisation des attributs, mieux mais pas tous satisfaits) :

using System;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Registration;
using System.Reflection;

namespace MEF2
{
    public interface IPluginMetadata
    {
        string Name { get; }
        string Version { get; }
    }

    public interface IPlugin
    {
        void Run();
    }

    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
    public class PluginMetadataAttribute : Attribute, IPluginMetadata
    {
        public string Name { get; set; }
        public string Version { get; set; }

        public PluginMetadataAttribute(string name, string version)
        {
            Name = name;
            Version = version;
        }
    }

    [PluginMetadata("Plugin1", "1.0.0.0")]
    public class Plugin1 : IPlugin
    {
        public void Run()
        {
            Console.WriteLine("Plugin1 runed");
        }
    }

    [PluginMetadata("Plugin2", "2.0.0.0")]
    public class Plugin2 : IPlugin
    {
        public void Run()
        {
            Console.WriteLine("Plugin2 runed");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var builder = new RegistrationBuilder();
            builder
                .ForTypesDerivedFrom<IPlugin>()
                .Export<IPlugin>(exportBuilder => {
                    exportBuilder.AddMetadata("Name", t => t.GetCustomAttribute<PluginMetadataAttribute>().Name);
                    exportBuilder.AddMetadata("Version", t => t.GetCustomAttribute<PluginMetadataAttribute>().Version);
                });

            var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly(), builder);

            using (var container = new CompositionContainer(catalog, CompositionOptions.DisableSilentRejection)) {
                var plugins = container.GetExports<IPlugin, IPluginMetadata>();

                foreach (var plugin in plugins) {
                    Console.WriteLine("{0}, {1}", plugin.Metadata.Name, plugin.Metadata.Version);
                    plugin.Value.Run();
                }
            }
        }
    }
}

La solution 3 contient le problème dans ce code :

.Export<IPlugin>(exportBuilder => {
    exportBuilder.AddMetadata("Name", t => t.GetCustomAttribute<PluginMetadataAttribute>().Name);
    exportBuilder.AddMetadata("Version", t => t.GetCustomAttribute<PluginMetadataAttribute>().Version);
})

Problèmes :

  1. Impossible d'annuler l'ajout de métadonnées en cas de métadonnées manquantes
  2. Code en double t.GetCustomAttribute<PluginMetadataAttribute>()
  3. Export<> ne pas fournir de filtre

Si quelqu'un connaît d'autres solutions, veuillez nous écrire.

2voto

Panos Rontogiannis Points 2560

El Une approche libre d'attributs pour configurer le MEF auquel vous faites référence dans votre autre question comprend un exemple sur la façon d'ajouter des métadonnées sans utiliser d'attribut.

L'exemple montre une utilisation de la fonction PartBuilder.ExportProperties qui prend un Action<PropertyInfo, ExportBuilder> en tant que paramètre et utiliser l'un des ExportBuilder.AddMetadata pour ajouter des métadonnées pour l'exportation spécifique.

Ce n'est pas la seule façon d'ajouter des métadonnées. Toutes les méthodes d'exportation de PartBuilder ont une surcharge qui prend un Action<> (ou une Action<,>) avec un paramètre ExportBuilder. Vous pouvez utiliser ces surcharges et ajouter vos métadonnées de manière similaire.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X