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

Sas*_*sha 3 .net c# clr appdomain

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

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

我创建了一个演示该问题的测试应用程序。插件程序集位于主机应用程序的子文件夹“ Plugin”中。Plugin文件夹包含插件实现DLL,插件声明接口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"));
}
Run Code Online (Sandbox Code Playgroud)

它引发异常:

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
Run Code Online (Sandbox Code Playgroud)

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

Lin*_*nky 5

我认为您的问题如下:

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);
Run Code Online (Sandbox Code Playgroud)

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

我建议您使用以下方法:

  1. 创建一个包含合同/接口的单独的程序集。该程序集是固定的,并且始终保持特定版本,并且永远不会过时,并且插件的每个版本都可以引用它。
  2. 编写一个程序集加载器,它是宿主应用程序的一部分,但也要在单独的程序集中。该程序集加载器程序集必须没有对应用程序其余部分的任何引用,因此您可以在新的appdomain中进行设置。
  3. 设置新的appdomain之后,加载您的AssemblyLoader实例。这将加载插件程序集并与其交互。
  4. 您的应用程序不会与程序集本身进行交互。您的应用程序通过appdomain边界调用AssemblyLoader,而AssemblyLoader调用插件

我不能说这是否是最好的版本,但是在每个插件都可以加载任何程序集的任何版本的环境中,这个版本对我来说效果很好。使用此设置,您几乎可以拒绝更改版本或更新,并且每个插件都可以使用所需的版本。

请让我知道这对你有没有用。