由于装配名称的MEF组合错误?

时间:2013-11-19 14:07:31

标签: c# .net plugins mef

提前感谢您审核此问题!

我正在使用MEF在项目中加载一些程序集。在我们更改包含界面的文件的名称之前,一切都运行良好。

为了更清楚,我将总结事情有效的场景,然后是事情不起作用的场景,然后专门显示异常以及在无法工作的场景中导致异常的代码。

以下是工作方案:

我们有一个名为IPlugin的接口,它在名为Common-1-0.dll的程序集中定义。

我们有一些针对Common-1-0.dll编译的插件程序集。

加载插件的应用程序是针对Common-1-0.dll进行编译的。

以下是非工作方案:

我们在一个名为Common-1-1.dll的程序集中定义了一个名为IPlugin的IPlugin。该接口没有从Common-1-0.dll更改。

我们有一些针对Common-1-0.dll编译的插件程序集。

加载插件的应用程序是针对Common-1-1.dll编译的。

现在问题:

当我在第二个场景中运行下面的代码时,我得到一个CompositionException(显示在下面的代码中)。它似乎是由于插件是针对Common-1-0.dll编译的,而尝试编写该组合的应用程序是针对Common-1-1.dll编译的。代码中的任何内容都没有在两个文件之间发生变化,只是名称。

所以我们希望能够加载针对任何程序集构建的插件,只要该程序集导出正确的接口,但我不确定我是否可以使用MEF执行此操作。这是我想通过这个问题得知的。

代码:

    private void LoadPlugins(string directory, string searchPattern = "", bool recursive = false)
    {
        Trace.Agent.Status("Loading plugin(s) from {0}{1}{2}", directory, Path.DirectorySeparatorChar, searchPattern);

        try
        {
            var directoryCatalog = string.IsNullOrEmpty(searchPattern)
                                           ? new DirectoryCatalog(directory)
                                           : new DirectoryCatalog(directory, searchPattern);
            _container = new CompositionContainer(new AggregateCatalog(directoryCatalog));

            _container.ComposeParts(this);
        }
        catch (CompositionException exc)
        {
            Trace.Agent.Exception(exc);
        }

        if (recursive)
        {
            foreach (string dir in Directory.GetDirectories(directory))
            {
                LoadPlugins(Path.Combine(directory, dir), recursive:true);
            }
        }
    }

CompositionException:

导出'TestPlugin.TestPlugin(ContractName =“Common.IPlugin”)'不能分配给'Common.IPlugin'。

1 个答案:

答案 0 :(得分:2)

我认为我找到了解决问题的方法,但无论如何都有点被黑了。

因此,如果您对程序集的命名方式不同,例如assembly1.0和assembly1.1会导致问题,因为您不能简单地将程序集重定向到新版本。 通常,您只需保留相同的程序集名称并增加版本号。这样您就可以重定向而不会出现任何问题(只要代码支持它)。

解决方案是通过附加到当前AppDomain的AssemblyResolve事件来破解程序集解析机制。

        AppDomain currentDomain = AppDomain.CurrentDomain;

        currentDomain.AssemblyResolve += new ResolveEventHandler(MyResolveEventHandler);

        LoadPlugins(currentDomain.BaseDirectory);

        var x = _container.GetExport<IPlugin>().Value;

在事件处理程序中,您只需返回“新”程序集

即可
    private static Assembly MyResolveEventHandler(object sender, ResolveEventArgs args)
    {
        if (args.Name.StartsWith("PluginCore1.0"))
        {
            return typeof(IPlugin).Assembly;
        }
        return null;
    }

这样做,也没有签名的程序集(没有公钥标记)。

要触发resolve事件,您仍然需要在app.config中定义程序集重定向:

<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
  <dependentAssembly>
    <assemblyIdentity name="PluginCore1.0"
                      culture="neutral" />

    <bindingRedirect oldVersion="1.0.0.0" newVersion="1.1.0.0" />
  </dependentAssembly>
</assemblyBinding>

同样,我强烈建议使用相同的程序集名称(没有版本后缀),只需使用应该正常工作的程序集重定向机制。