2

My previous post contains attempt use attribute-free (convention based) approach to configure MEF: MEF 2: import many.
But it contains export metadata attribute usage in the class PluginMetadataAttribute needed for lazy initialization plugin by condition (specific name, version).
How to get rid of ExportAttribute dependency?

Community
  • 1
  • 1
anpv
  • 179
  • 1
  • 13

2 Answers2

3

I found three solution.
Solution 1 (using class constant fields, poor 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 (using class properties, poor 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");
    }
}

And get properties values by method described this: https://stackoverflow.com/a/11162876/1986524

Solution 3 (using attributes, better but not all happy):

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

Solution 3 contains problem in this code:

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

Problems:

  1. Can't cancel add metadata in case of missing metadata
  2. Duplicate code t.GetCustomAttribute<PluginMetadataAttribute>()
  3. Export<> don't provided filter

If anyone knows of other solutions please write.

Community
  • 1
  • 1
anpv
  • 179
  • 1
  • 13
  • I prefer the third solution too. 1) You can add an if statement inside the lambda and only add the metadata if the attribute exists. 2) Instead of adding two metadata add one. The whole PluginMetadata object! 3) What kind of filtering do you have in mind? – Panos Rontogiannis Jan 25 '13 at 08:04
  • @PanosRontogiannis 1. Yes, I'm add check: `t => { var attr = t.GetCustomAttribute(); return (attr == null ? null : attr.Name); }` but don't know how cancel add metadata if attribute not exists 2. I don'n know how add one metadata, because AddMetadata method takes first required parameter `string name`. And this variant `exportBuilder.AddMetadata(null, new PluginMetadataAttribute("Plugin 1", "1.0.0.0"))` don't works. Please tell me how to do it? 3. I want to filter the classes in which the attribute is present – anpv Jan 25 '13 at 08:41
2

The An Attribute-Free Approach to Configuring MEF article that you reference in your other question includes an example on how to add metadata without using an attribute.

The example shows a use of the PartBuilder.ExportProperties overload that takes an Action<PropertyInfo, ExportBuilder> as a parameter and use one of the ExportBuilder.AddMetadata overloads to add metadata for the specific export.

This is not the only way to add metadata. All export methods of PartBuilder have an overload that take an Action<> (or an Action<,>) with an ExportBuilder param. You can use these overloads and add your metadata in a similar way.

Panos Rontogiannis
  • 4,154
  • 1
  • 24
  • 29
  • As far as I understand I have to use the properties of the classes inherited interface IPlugin: public class Plugin1 : IPlugin { public string Name { get { return "Plugin1"; } } public string Version { get { return "1.0.0.0"; } } But how to extract data from this properties and pass to AddMetadata from ExportProperties Action<,>? – anpv Jan 23 '13 at 03:45