mef enum出口和建筑

时间:2011-09-12 12:47:55

标签: c# architecture mef

我想使用mef制作我的插件子系统,但我几乎没有问题,因为我是csharp和mef的新手(

我想做什么:

  1. 每个插件都可以创建自己的接口IPlugin1,IPlugin2 ......
  2. 每个接口必须具有指定的功能Load,Unload
  3. 使用mef我想枚举所有导出并调用加载/卸载
  4. 问题:

    一个。我的解决方案是否良好,如果没有,我如何改进它(通过使用其他东西)?

    湾如何使用mef枚举所有导出并调用指定的接口函数?

    我会感谢所有链接和评论家。谢谢大家。

2 个答案:

答案 0 :(得分:1)

如果您希望所有插件接口都遵循自己的接口,那么您应该提供一个接口供他们扩展:

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

然后,当插件创建自己的接口时,它们应该从您公开提供的接口扩展:

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

现在,为了获得在MEF中导出的接口集合,您必须将接口标记为InheritedExport:

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

这表明任何以某种方式从IMyInterface扩展的类都将作为一种IMyInterface导出,甚至在树下:

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

最后,在代码中的某个位置(如果适用),您可以导入一组IMyInterface实例。

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

如果所有内容都正确连接,Plugins将是一个类的枚举,通过继承,导出为IMyInterface


资源:

答案 1 :(得分:1)

基于@m-y的回答,我认为拥有一个通用界面是您可以使用的最佳设计。这是利用您可以应用于插件的一组通用操作的最简单方法。我会考虑的是稍微改进一下:

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

通过强制执行Dispose方法,您可以自动控制CompositionContainer的生命周期管理功能。您的所有卸载代码都可以进入,这是一个示例插件:

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

Dispose模式允许使用标准化机制来清理代码。您的CompositionContainer实例将跟踪此项目,并在处理时清理它。

现在,我想要描述的是MEFContrib中一个名为InterceptingCatalog的新增内容。这个目录允许你做的是注册拦截策略,它可以让你在返回到你的调用代码之前访问一个导出的值。一个这样的用法可能是自动确保在第一次导出实例时在基础Initialise接口上调用IPlugin

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

        return value;
    }
}

让我们把这一切联系起来:

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

如果你运行它,你会注意到我们ConsoleLogger在我们第一次从容器中获取时会自动初始化。我们不需要担心它会再次初始化,它只会在创建导出的实例时这样做,这意味着它服从单例和非单例场景。

您可能会想到这可能是一种过度杀伤,但它实际上是一个非常优雅的解决方案,可以让您的零件自动启动,并在不再需要时处理掉。

当然,如果你想要对你的部件初始化方式进行细致的控制,你可以用你自己编写的方法来管理它们,你只需要考虑插件状态。

您可以在Piotr Włodek's blog

了解更多关于InterceptingCatalog的信息