关于c#:由于程序集名称的MEF组合错误?

MEF Composition Error Because of Assembly Names?

提前感谢您审阅此问题!

我正在使用MEF在项目中加载一些程序集。在我们更改包含接口的文件名之前,一切都很好。

为了更清楚地说明这一点,我将总结出工作的场景,然后总结出工作不正常的场景,然后具体说明异常和导致不工作场景中异常的代码。

以下是工作场景:

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

我们有一些插件程序集是根据common-1-0.dll编译的。

加载插件的应用程序是根据common-1-0.dll编译的。

以下是非工作场景:

我们有一个名为iplugin的程序集,它在名为common-1-1.dll的程序集中定义。接口没有从common-1-0.dll更改。

我们有一些插件程序集是根据common-1-0.dll编译的。

加载插件的应用程序是根据common-1-1.dll编译的。

现在的问题是:

当我在第二个场景中运行下面的代码时,会得到一个compositionexception(显示在下面的代码下)。这似乎是因为插件是根据common-1-0.dll编译的,而试图进行组合的应用程序是根据common-1-1.dll编译的。代码中的任何内容在两个文件之间都没有改变,只有名称。

因此,我们希望能够加载针对任何程序集构建的插件,只要该程序集导出正确的接口,但我不确定是否可以用MEF实现。这就是这个问题的结果,我想知道的。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
    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);
            }
        }
    }

组合例外:

导出"testplugin.testplugin(contractname="common.iplugin")不能分配给类型"common.iplugin"。


我想我找到了解决你的问题的方法,虽然有点老套,但无论如何。

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

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

1
2
3
4
5
6
7
        AppDomain currentDomain = AppDomain.CurrentDomain;

        currentDomain.AssemblyResolve += new ResolveEventHandler(MyResolveEventHandler);

        LoadPlugins(currentDomain.BaseDirectory);

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

在事件处理程序中,您可以简单地返回"new"程序集

1
2
3
4
5
6
7
8
    private static Assembly MyResolveEventHandler(object sender, ResolveEventArgs args)
    {
        if (args.Name.StartsWith("PluginCore1.0"))
        {
            return typeof(IPlugin).Assembly;
        }
        return null;
    }

这也适用于没有签名的程序集(没有公钥标记)。

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

1
2
3
4
5
6
7
  <dependentAssembly>
    <assemblyIdentity name="PluginCore1.0"
                      culture="neutral" />

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

同样,我强烈建议使用相同的程序集名称(不带版本后缀),并且只使用程序集重定向机制,这种机制应该可以正常工作。