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

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

4

2 回答 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 作为您自己的目录目录实施的指南。

于 2013-02-12T21:44:37.890 回答
1

一旦 dll 在应用程序域中加载,它就无法从该域中卸载。只能卸载整个域。因此,要实现您所追求的并不容易。可以不断扫描更改并加载新副本并重新指向调用(通过这种方式,您将在域中积累越来越多的无用程序集),但我不相信这是 MEF 开箱即用的实现. 换句话说,您观察到的行为是设计使然。

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

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

于 2013-02-12T21:19:21.747 回答