在进行目录刷新时遇到错误,新的dll未被使用。

6

我正在尝试使用MEF创建一个POC,在其中我需要在一个已运行的项目中动态加载dll。为此,我创建了一个控制台应用程序和一个类库项目。

控制台应用程序的代码如下:

namespace MefProjectExtension
{
    class Program
    {
        DirectoryCatalog catalog = new DirectoryCatalog(@"D:\MefDll", "*.dll");

        [Import("Method1", AllowDefault = true, AllowRecomposition = true)]
        public Func<string> method1;

        static void Main(string[] args)
        {
            AppDomainSetup asp = new AppDomainSetup();
            asp.ShadowCopyFiles = "true";

            AppDomain sp = AppDomain.CreateDomain("sp",null,asp);

            string exeassembly = Assembly.GetEntryAssembly().ToString();
            BaseClass p = (BaseClass)sp.CreateInstanceAndUnwrap(exeassembly, "MefProjectExtension.BaseClass");
            p.run();
        }
    }


    public class BaseClass : MarshalByRefObject
    {
        [Import("Method1",AllowDefault=true,AllowRecomposition=true)]
        public Func<string> method1;

        DirectoryCatalog catalog = new DirectoryCatalog(@"D:\MefDll", "*.dll");

        public void run()
        {
            FileSystemWatcher sw = new FileSystemWatcher(@"D:\MefDll", "*.dll");
            sw.NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.Size;
            sw.Changed += onchanged;

            CompositionContainer container = new CompositionContainer(catalog);

            container.ComposeParts(this);

            Console.WriteLine(this.method1());

            sw.EnableRaisingEvents = true;

            Console.Read();
        }

        void onchanged(object sender, FileSystemEventArgs e)
        {
            catalog.Refresh();

            Console.WriteLine(this.method1());
        }
    }
}

满足导入外观的库项目如下所示 -
namespace MefLibrary
{
    public interface IMethods
    {
         string Method1();  
    }

    public class CallMethods : IMethods
    {
        [Export("Method1")]
        public string Method1()
        {
            return "Third6Hello";
        }
    }
}

一旦我编译了库项目(MefLibrary)并将dll放置在D:\ MefDll位置,并首次运行控制台应用程序,我将看到输出如下: 屏幕上显示Third6hello 但是现在,如果我更改method1的实现并使其返回“third7hello”,则构建MEF Library项目并在我的控制台应用程序正在运行时替换为D:\ MefDll,即使在调用catalog refresh后,onchanged处理程序仍然会打印 屏幕上显示Third6hello 而不是屏幕上显示third7hello 是否有人知道原因? 如果是,请提供帮助。
2个回答

9
DirectoryCatalog.Refresh 只会添加新的或删除现有的程序集。它不会更新程序集。一个简单的解决方法是:
  1. 将更新后的程序集移动到一个临时文件夹。
  2. 调用 DirectoryCatalog.Refresh。这将删除包含在程序集中的部件。
  3. 将程序集移回已观察的文件夹。
  4. 再次调用 DirectoryCatalog.Refresh。这将添加包含在程序集中的更新的部件。

注意:

  • 为此工作,您的“插件”程序集必须具有强名称不同的版本号AssemblyVersionAttribute)。这是必需的,因为使用 DirectoryCatalog.Refresh 删除部件时,实际程序集不会被卸载。只有当整个应用程序域被卸载时,才能卸载程序集。因此,如果 DirectoryCatalog.Refresh 发现新的程序集,则会使用程序集文件路径创建一个 AssemblyCatalog。然后,AssemblyCatalog 将调用 Assembly.Load 加载程序集。但是,如果该方法加载与已加载程序集具有相同 AssemblyName.FullName 的程序集,则不会加载该程序集。
  • 请确保我提及的步骤不会触发另一个 FileSystemWatcher.Changed 事件。例如,您可以使用这种方法
  • 您的程序将需要对已观察文件夹拥有写入访问权限。如果您在 %ProgramFiles% 文件夹中部署,这可能会有问题。
  • 如果您需要线程安全性,则可以考虑使用带有 CompositionOption.IsThreadSafe 标志的 CompositionContainer 创建。

如我所述,这是一种解决方法。另一种方法是下载MEF的源代码并使用 DirectoryCatalog.cs 作为自己的目录编目实现指南。


更改程序集版本并签名程序集就可以解决问题了。谢谢! - Ramesh Durai

1

一旦一个 DLL 被加载到一个应用程序域中,它就无法从该域中卸载。只有整个域可以被卸载。因此,实现你想要的功能并不容易。可能会不断扫描更改并加载新副本以及重定向调用(这样将在您的域中累积越来越多无用的程序集),但我不认为 MEF 在这方面提供了开箱即用的实现。换句话说,你所观察到的行为是有意的。

这种实现还可能因状态而变得棘手且容易出错。想象一下,你设置了一个旧 DLL 的类实例中的字段,并将其分配给一个变量。然后新的 DLL 出现了。旧实例和它的字段会发生什么?显然,它们将保持不变,现在你在内存中使用的是不同版本的插件。真是一团糟!

如果你感兴趣,这里是为什么没有 Assembly.Unload 方法的原因,以及可能的(概念性)解决方法


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接