Search code examples
c#pluginsreflectionmef.net-assembly

MEF, why are identical duplicates of one and the same exported plugin created?


(1) Using the code below I get exactly 2 items in my containers of one and the same exported plugin and I wonder why:

(2) Additional question which I really cannot implement: How can I extend the framework to handle different plugin types (such as having several imports of different types, or one import that stores all plugins in a dynamic IEnumerable or so). I want to provide in my static wrapper class one generic method that returns the discovered plugin as a function of type and matching meta data.

The exported plugin (which resides in a separate dll and whose location is pointed to when the DirectoryCatalog is built.

[Export(typeof(IPlugin))] //<---- If this line is commented out then only one item is imported (why?)
[PluginAttribute(typeof(StrategyPlugin_Test1), "StrategyPlugin", "Plugin1")]
public class StrategyPlugin_Test1 : IPlugin
{
    public void DoSomething()
    {
        Console.WriteLine("I do something");
    }
}

The following code defines the strongly typed meta data and imports as well as a static class that performs the MEF functions and is supposed to hold the discovered plugins:

[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class PluginAttribute : ExportAttribute
{
    public Type PluginType { get; set; }
    public string PluginGroupName { get; set; }
    public string PluginName { get; set; }

    public PluginAttribute(Type pluginType, string pluginGroupName, string pluginName) : base(typeof(IPlugin))
    {
        PluginType = pluginType;
        PluginGroupName = pluginGroupName;
        PluginName = pluginName;
    }
}

public interface IPluginAttribute
{
    Type PluginType { get; }
    string PluginGroupName { get; }
    string PluginName { get; }
}

public interface IPlugin
{
    void DoSomething();
}

public class PluginDefinition
{
    [ImportMany(typeof(IPlugin))]
    public IEnumerable<Lazy<IPlugin, IPluginAttribute>> Plugins { get; set; }

    public PluginDefinition()
    {

    }
}

Here the static class that wraps some of the core MEF stuff:

public static class PluginManager
{
    private static PluginDefinition PluginDefinitions { get; set; }

    static PluginManager()
    {}

    public static void Configure(PluginDefinition pluginDefinitions, IEnumerable<string> pluginDirectories)
    {
        AggregateCatalog aggregateCatalog = new AggregateCatalog(new DirectoryCatalog(pluginDirectories.FirstOrDefault()));
        CompositionContainer container = new CompositionContainer(aggregateCatalog);
        container.ComposeParts(pluginDefinitions);

        //store plugin definition
        PluginDefinitions = pluginDefinitions;


    }

    public static T GetPlugin<T>(string pluginName, string pluginGroupName) where T : class
    {
        //how to implement this given type of T could be any of the plugin types ...
        //...provided for in an extended PluginDefinition class?

        return null;
    }

}

Solution

  • The reason behind the duplicate exports is the fact that you are deriving your custom export metadata attribute from ExportAttribute. This means that when you decorate a class member with PluginAttribute, you do not need to add the ExportAttribute. MEF will look for attributes assignable to ExportAttribute and it will find one in your PluginAttribute.

    For the other question regarding plug-in types, MEF allows for multiple exports on the same type. You can have one export of type IPlugin and another more specialized like you were doing in your code:

    [Export(typeof(IPlugin))] //<---- If this line is commented out then only one item is imported (why?)
    [PluginAttribute(typeof(StrategyPlugin_Test1), "StrategyPlugin", "Plugin1")]
    public class StrategyPlugin_Test1 : IPlugin
    

    And then have different imports for each exported type. You can have an IEnumerable<IPlugin> import and then on another property a StrategyPlugin_Test1 import.