在目录刷新期间遇到错误,不使用新的dll

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

项目。

控制台应用程序项目的代码如下 –

namespace MefProjectExtension { class Program { DirectoryCatalog catalog = new DirectoryCatalog(@"D:\MefDll", "*.dll"); [Import("Method1", AllowDefault = true, AllowRecomposition = true)] public Func 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 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()); } } } 

满足import的图书馆项目如下

 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库项目并在D:\ MefDll处替换,而我的控制台应用程序运行onchanged处理程序,即使在调用目录刷新后在屏幕上打印Third6hello而不是third7hello

是否有人知道这是什么原因,如果有,请帮忙。

DirectoryCatalog.Refresh仅添加新的或删除现有的程序集。 它不会更新程序集。 一个粗略的解决方法是:

  1. 将更新的程序集移动到临时文件夹。
  2. 致电DirectoryCatalog.Refresh 。 这将删除assembly中包含的零件。
  3. 将组件移回监视文件夹
  4. 致电DirectoryCatalog.Refresh 。 这将添加程序集中包含的更新部件。

注意:

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

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

在app域中加载dll后,无法从该域卸载。 只能卸载整个域。 因此,实现你所追求的并不容易。 可以不断扫描更改并加载新副本并重新调用这些调用(您将以这种方式在域中累积越来越多无用的程序集),但我不相信这是MEF开箱即用的东西。 换句话说,您观察的行为是设计的。

由于状态,执行此操作也很棘手且容易出错。 想象一下,您在旧DLL的类实例中设置了一个字段并将其分配给变量。 然后新的dll通过。 旧实例及其字段会发生什么? 显然它们将保持不变,现在您在内存中使用了不同版本的插件。 真是一团糟!

如果您对此感兴趣, 那么就是没有Assembly.Unload方法的原因。 可能的(概念性)解决方法 。