0

我正在开发一个服务在后台连续运行的解决方案,并且可以在运行时添加/删除插件 DLL。该服务将在需要时加载必要的插件,运行它们并卸载它们。这是当前给我带来麻烦的卸载部分:一旦某个类第一次成功加载(变量 tc),即使更新了 DLL 文件,它也永远不会重新加载。我想我没有正确卸载类/程序集/应用程序域,所以我很感激一些关于走最后一英里的建议。

编辑:我更新了帖子以反映最近的代码更改,并解释了卸载何时没有效果:问题没有出现在 Linux Ubuntu(通过 Mono)上,但它出现在 Windows 2008 Server 上,当我试图用较新的文件版本替换某个插件 DLL。似乎 .NET 框架已将程序集缓存在某处,并且无需重新加载它就很高兴。DLL 文件名没有改变,但 File Version 属性不同,所以我希望运行时将先前加载的 DLL 版本与正在加载的版本进行比较,如果版本号不同,则使用较新的版本。如果我稍微更改代码以从具有不同名称的 DLL 文件加载程序集,则重新加载会按预期进行。

using System;
using System.Reflection;

namespace TestMonoConsole
{
    public interface ITestClass
    {
        void Talk();
    }
    class MainClass
    {
        public static void Main (string[] args)
        {
            string pluginPath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);

            string classAssembly = "TestClass";
            string className = "TestMonoConsole.TestClass";
            string command = "";
            do
            {
                try
                {
                    System.AppDomain domain = System.AppDomain.CreateDomain(classAssembly);
                    string pluginAssemblyFile = pluginPath + "/" + classAssembly + ".dll";
                    System.IO.StreamReader reader = new System.IO.StreamReader(pluginAssemblyFile, System.Text.Encoding.GetEncoding(1252), false);
                    byte[] b = new byte[reader.BaseStream.Length];
                    reader.BaseStream.Read(b, 0, System.Convert.ToInt32(reader.BaseStream.Length));
                    domain.Load(b);
                    reader.Close();
                    ITestClass tc = (ITestClass) Activator.CreateInstance(domain, classAssembly, className).Unwrap();
                    tc.Talk();
                    System.AppDomain.Unload(domain);
                }
                catch (System.IO.FileNotFoundException e)
                {
                    Console.WriteLine (String.Format("Error loading plugin: assembly {0} not found", classAssembly));
                }
                command = Console.ReadLine();
            } while (command == "");
        }
    }
}
4

5 回答 5

2

您正在将类型直接创建到主机域中。使用 Activator.CreateInstance 时需要根据http://msdn.microsoft.com/en-us/library/ms224132(v=vs.90).aspx指定域

于 2012-07-05T19:23:52.723 回答
0

您正在自己的 AppDomain 中实例化插件类型。在多次实现插件框架之后,我强烈建议考虑使用 MEF(托管扩展框架)作为解决方案,因为它可以处理这些常见的代码隔离问题。如果您不想要 MEF,一种方法是实现一个“远程控制”类,该类将充当应用程序域之间的通信器。您可以在远程类上调用一个方法,该方法将在辅助应用程序域中实例化和运行代码。

MEF 文档

于 2012-07-05T19:31:26.610 回答
0

无法从 AppDomain 卸载类型和程序集。因此,您需要创建新的 AppDomain、加载类型、完成所有工作,然后卸载该域。

于 2012-07-05T19:30:32.467 回答
0

此外,您可以利用托管加载项框架(MAF、System.AddIn)为您执行此操作,而不是重新发明此机制。在这里查看快速入门。

于 2012-07-05T19:27:31.183 回答
0

根据这篇 MSDN 文章,CLR 默认不使用卷影复制。您可以尝试按照以下步骤启用它:

  1. 创建一个AppDomainSetup
  2. 在 AppDomainSetup 实例中将 ShadowCopyFiles 设置为 true。
  3. 在 AppDomainSetup 实例中,将 ShadowCopyFilesDirectories 设置为包含您希望能够在运行时覆盖的程序集的目录路径。这可能只是插件程序集所在的目录。
  4. 在 AppDomainSetup 实例中,根据您的需要设置其他所有内容。
  5. 使用以 AppDomainSetup 作为参数的 AppDomain.CreateAppDomain重载之一。
  6. 尝试更换插件程序集。

它适用于 Mono 而不是 CLR (Windows) 的事实可能意味着CLI未指定默认情况下是否应启用卷影复制。

于 2012-07-13T17:34:50.430 回答