mef enum exports and architecture

醉酒当歌 提交于 2019-12-30 07:48:47

问题


I want to make my plugin subsystem using mef but i have few question because i am newbie in csharp and mef (

What i want to do:

  1. every plugin can create its own interface IPlugin1, IPlugin2 ...
  2. each of these interfaces must have specified functions Load, Unload
  3. using mef i want to enum all exports and call Load/Unload

The questions:

a. Is my solution good and if no how can i improve it (by using something else)?

b. How to enum all exports using mef and call specified interface function?

I would appreciate for all links and critics. Thanks for all.


回答1:


Building on @m-y's answer, I do think having a common interface is the best design you can go with. It is the easiest way to leverage a common set of operations you could apply to your plugins. What I would consider though, is a slight refinement:

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

By enforcing the Dispose method is implemented, you part can automatically be controlled by the lifetime management features of the CompositionContainer. All your unloading code can go in there, here is a sample plugin:

public interface ILogger : IPlugin
{
    void Log(string message);
}

[Export(typeof(ILogger))]
public class ConsoleLogger : ILogger
{
    void IPlugin.Initialise()
    {
        Console.WriteLine("Initialising plugin...");
    }

    public void Log(string message)
    {
        Console.WriteLine(message);
    }

    public virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            Console.WriteLine("Disposing plugin...");
        }
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

The Dispose pattern allows a standardised mechanism for cleaning up your code. Your CompositionContainer instance will keep track of this item, and clean it up when it is disposed.

Now, what I wanted to describe is a nice addition in MEFContrib called the InterceptingCatalog. What this catalog allows you to do, is register interception strategies which could give you access to an exported value before it is returned to your calling code. One such use could be to automatically ensure that Initialise is called on the base IPlugin interface when the instance is being exported for the first time:

public class InitialisePluginStrategy : IExportedValueInterceptor
{
    public object Intercept(object value)
    {
        var plugin = value as IPlugin;
        if (plugin != null)
            plugin.Initialise();

        return value;
    }
}

Let's tie all this up:

static void Main(string[] args)
{
    var catalog = new AssemblyCatalog(typeof(Program).Assembly);

    var configuration = new InterceptionConfiguration()
        .AddInterceptor(new InitialisePluginStrategy());

    var interceptingCatalog = new InterceptingCatalog(catalog, configuration);

    var container = new CompositionContainer(interceptingCatalog);

    var logger = container.GetExportedValue<ILogger>();

    logger.Log("test");

    Console.ReadKey();
}

If you run that, you'll notice our ConsoleLogger is automatically initialised the first time we get it from the container. We don't need to worry about it being initialised again, it will only do so when it is creating the exported instance, which means it obeys singleton and non-singleton scenarios.

You might be thinking at this point that this could be overkill, but its actually quite an elegant solution to enable your parts to be automatically started, and disposed of when no longer needed.

Of course, if you want fine grained control over how your parts are initialised, you could just manage these in a method you write yourself, you just need to consider the plugin state when you do so.

You can read up about the InterceptingCatalog more at Piotr Włodek's blog




回答2:


If you want all your plugin interfaces to adhere to an interface of their own then you should provide an interface for them to extend:

public interface IMyInterface
{
    void Load();
    void Unload();
}

Then, when the plugs create their own interfaces, they should extend from your publicly provided interface:

public interface IPlugin1 : IMyInterface
{
    void DoPlugin1Func();
}

Now, in order to get a collection of interfaces that are exported in MEF you'd have to tag your interface as an InheritedExport:

[InheritedExport(typeof(IMyInterface))]
public interface IMyInterface { ... }

This states that any class that somehow extends from the IMyInterface will get exported as a type of IMyInterface, even down the tree:

//This class will be exported as an IMyInterface
public class PluginImplementation1 : IPlugin1 { ... }

Finally, somewhere in your code (where applicable), you can import a collection of IMyInterface instances.

public class SomeClass
{
    [ImportMany(typeof(IMyInterface))]
    private IEnumerable<IMyInterface> Plugins { get; set; }
}

If everything is wired up properly, Plugins will be an enumeration of classes that, through inheritance, exported out as IMyInterfaces.


Resources:

  • InheritedExport Example
  • ImportMany Example


来源:https://stackoverflow.com/questions/7388115/mef-enum-exports-and-architecture

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!