c# - 将不同版本的程序集加载到单独的 AppDomain 中

标签 c# .net clr appdomain

我正在实现支持插件的应用程序。当前,当我尝试加载主机应用程序和插件都使用的通用程序集时出现问题:主机应用程序应使用该程序集的一个版本,而插件使用另一个版本。这是由应用程序升级决定的进程 - 插件可以独立于主机应用程序进行更新。

每个程序集都已签名,因此我使用强名称来加载程序集。

我创建了一个演示问题的测试应用程序。插件程序集位于主机应用程序的子文件夹“插件”中。 Plugin文件夹包含插件实现DLL、插件声明接口(interface)DLL和CommonLib DLL。主机应用程序还使用插件声明 DLL 和 CommonLib DLL(位于文件夹树中的上一级)。

插件加载源码如下:

static void Main(string[] args)
{
    Console.WriteLine("Host   says: {0}", GreetingManager.SayHello("Sasha"));
    Console.WriteLine("Host   says: {0}", GreetingManager.SayGoodBye("Sasha"));
    var pluginDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugin");
    var pluginFile = Directory.GetFiles(pluginDir).First(f => f.EndsWith("Plugin.dll"));

    Assembly asmr = Assembly.ReflectionOnlyLoadFrom(pluginFile);

    AppDomain pluginDomain = AppDomain.CreateDomain("Plugin", null, pluginDir, "", false);

    pluginDomain.Load(asmr.FullName);

    Assembly asm = pluginDomain.GetAssemblies().First(a => a.GetName().Name == "Plugin");
    Type p = asm.GetTypes().First(t => t.GetInterfaces().Contains(typeof(IPlugin)));

    var pluginInstance = (IPlugin)pluginDomain.CreateInstanceAndUnwrap(asm.FullName, p.FullName);
    Console.WriteLine("Plugin says: {0}", pluginInstance.TransformString("Sasha"));
}

抛出异常:

Unhandled Exception: System.IO.FileLoadException: Could not load file or assembly 'CommonLib, Version=1.0.9.0, Culture=neutral, PublicKeyToken=73c7c163a33b622c' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
   at Plugin.MyPlugin.TransformString(String str)
   at PluginBackwardCompatibilityTest.Program.Main(String[] args) in D:\Projects\Test\PluginBackwardCompatibilityTest\PluginBackwardCompatibilityTest\Program.cs:line 37

据我所知,插件仍会尝试在主机应用程序文件夹中查找 CommonLib 程序集(忽略我在创建 AppDomain 时提供的 appBasePath 参数),但会在那里找到旧版本 1.0.8 并生成此错误。 如何强制插件从插件文件夹加载引用的程序集 - 使用强名称时无法指定要加载的完整程序集路径。

最佳答案

我认为你的问题如下:

Assembly asm = pluginDomain.GetAssemblies().First(a => a.GetName().Name == "Plugin");
Type p = asm.GetTypes().First(t => t.GetInterfaces().Contains(typeof(IPlugin)));
var pluginInstance = (IPlugin)pluginDomain.CreateInstanceAndUnwrap(asm.FullName, p.FullName);

这样您就可以将更新的/过时的类型引用加载到主 AppDomain 中(其中已经加载了不同版本的程序集)。

我建议您使用以下方法:

  1. 创建一个包含契约(Contract)/接口(interface)的单独程序集。这个程序集是固定的,并且一直保持在一个特定的版本,它永远不会过时,你的插件的每个版本都可以引用它。
  2. 编写一个程序集加载程序,它是您的主机应用程序的一部分,但也在一个单独的程序集中。此程序集加载器程序集不得对您的应用程序的其余部分有任何引用,以便您可以在新的应用程序域中对其进行设置。
  3. 设置新的应用程序域后,加载您的 AssemblyLoader 实例。这一个加载插件程序集并与之交互。
  4. 您的应用程序不与程序集本身交互。您的应用程序通过 appdomain 边界调用您的 AssemblyLoader,AssemblyLoader 调用插件

我不能说这是否是最好的版本,但在每个插件都可以加载任何程序集的任何版本的环境中,这个版本对我来说非常好用。使用此设置,您几乎可以抵制更改版本或更新,并且每个插件都可以使用它想要的版本。

让我知道它是否适合您。

关于c# - 将不同版本的程序集加载到单独的 AppDomain 中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21941474/

相关文章:

c# - De Bruijn 算法二进制数字计数 64 位 C#

c# - 在 C# 中从图像制作视频的工作方式

c# - 是否有比 "fire and forget"更好、更可靠的模式来同时处理可变数量的异步任务?

.net - 哪种泛型参数约束组合将使 C# 编译器为 GenericParamConstraint 元数据表生成行?

.net - .NET CLR JIT 是否每次都编译每个方法?

c# - EF中的主键最大值

c# - 升级Project Unity 2019-> 2020

c# - 获取分隔符以填充剩余空间

c# - 如何使用 .NET 的 MongoDB 驱动程序的 updateOne() 更新但不替换文档

c# - 如果我的类实现了 IEqualityComparer<T>,我是否应该实现非泛型 GetHashCode 和 Equals?