【问题标题】:MEF + Plug-In not updatingMEF + 插件未更新
【发布时间】:2011-02-07 04:52:10
【问题描述】:

我已经在 MEF Codeplex 论坛上问过这个问题,但还没有得到回复,所以我想试试 StackOverflow。如果有人感兴趣,这是原始帖子(这只是它的副本):

MEF Codeplex

“首先让我说我对 MEF 完全陌生(今天才发现它)并且到目前为止对它非常满意。但是,我遇到了一个非常令人沮丧的问题。我正在创建具有插件架构的应用程序,并且插件将仅存储在单个 DLL 文件中(或编码到主应用程序中)。DLL 文件需要能够在运行时重新编译,并且应用程序应该识别这一点并且重新加载插件(我知道这很困难,但这是一个要求)。为此,我采用了 http://blog.maartenballiauw.be/category/MEF.aspx 覆盖的方法(查找 WebServerDirectoryCatalog)。基本上这个想法是“监控插件文件夹,复制新的/modified 程序集到 Web 应用程序的 /bin 文件夹,并指示 MEF 从那里加载其导出。” 这是我的代码,这可能不是正确的方法,但这是我在网上的一些示例中找到的:

        main()...
    string myExecName = Assembly.GetExecutingAssembly().Location;
        string myPath = System.IO.Path.GetDirectoryName(myExecName);
        catalog = new AggregateCatalog();
        pluginCatalog = new MyDirectoryCatalog(myPath + @"/Plugins");
        catalog.Catalogs.Add(pluginCatalog);


        exportContainer = new CompositionContainer(catalog);

        CompositionBatch compBatch = new CompositionBatch();
        compBatch.AddPart(this);
        compBatch.AddPart(catalog);
        exportContainer.Compose(compBatch);

    private FileSystemWatcher fileSystemWatcher;
    public DirectoryCatalog directoryCatalog;
    private string path;
    private string extension;

    public MyDirectoryCatalog(string path)
    {
        Initialize(path, "*.dll", "*.dll");
    }

    private void Initialize(string path, string extension, string modulePattern)
    {
        this.path = path;
        this.extension = extension;
        fileSystemWatcher = new FileSystemWatcher(path, modulePattern);
        fileSystemWatcher.Changed += new FileSystemEventHandler(fileSystemWatcher_Changed);
        fileSystemWatcher.Created += new FileSystemEventHandler(fileSystemWatcher_Created);
        fileSystemWatcher.Deleted += new FileSystemEventHandler(fileSystemWatcher_Deleted);
        fileSystemWatcher.Renamed += new RenamedEventHandler(fileSystemWatcher_Renamed);
        fileSystemWatcher.IncludeSubdirectories = false;
        fileSystemWatcher.EnableRaisingEvents = true;
        Refresh();
    }
    void fileSystemWatcher_Renamed(object sender, RenamedEventArgs e)
    {
        RemoveFromBin(e.OldName);
        Refresh();
    }
    void fileSystemWatcher_Deleted(object sender, FileSystemEventArgs e)
    {
        RemoveFromBin(e.Name);
        Refresh();
    }
    void fileSystemWatcher_Created(object sender, FileSystemEventArgs e)
    {
        Refresh();
    }
    void fileSystemWatcher_Changed(object sender, FileSystemEventArgs e)
    {
        Refresh();
    }
    private void Refresh()
    {
        // Determine /bin path 
        string binPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins");
        string newPath = "";
        // Copy files to /bin 
        foreach (string file in Directory.GetFiles(path, extension, SearchOption.TopDirectoryOnly))
        {
            try
            {
                DirectoryInfo dInfo = new DirectoryInfo(binPath);
                DirectoryInfo[] dirs = dInfo.GetDirectories();
                int count = dirs.Count() + 1;
                newPath = binPath + "/" + count;
                DirectoryInfo dInfo2 = new DirectoryInfo(newPath);
                if (!dInfo2.Exists)
                    dInfo2.Create();

                File.Copy(file, System.IO.Path.Combine(newPath, System.IO.Path.GetFileName(file)), true);
            }
            catch
            {
                // Not that big deal... Blog readers will probably kill me for this bit of code :-) 
            }
        }
        // Create new directory catalog 
        directoryCatalog = new DirectoryCatalog(newPath, extension);
        directoryCatalog.Refresh();
    }
    public override IQueryable<ComposablePartDefinition> Parts
    {
        get { return directoryCatalog.Parts; }
    }
    private void RemoveFromBin(string name)
    {
        string binPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "");
        File.Delete(Path.Combine(binPath, name));
    }

所以这一切实际上都有效,并且在 main 中的代码结束后,我的 IEnumerable 变量实际上填充了 DLL 中的所有插件(如果您按照代码位于 Plugins/1 中,以便我可以修改dll 在插件文件夹中)。 所以现在我应该能够重新编译插件 DLL,将其放入 Plugins 文件夹,我的 FileWatcher 检测到它已更改,然后将其复制到文件夹“2”并且 directoryCatalog 应该指向新文件夹。这些所有 确实有效!问题是,即使似乎每件事都指向了正确的位置,但我的 IEnumerable 变量永远不会使用新插件进行更新。如此接近,却又如此遥远!有什么建议? 我知道这样做的缺点,实际上没有 dll 被卸载并导致内存泄漏,但它是一个 Windows 应用程序,可能每天至少启动一次,并且插件不太可能改变 经常这样,但客户端仍然要求它在不重新加载应用程序的情况下执行此操作。谢谢!

感谢大家提供的任何帮助,无法弄清楚这一点让我发疯了。”

【问题讨论】:

    标签: c# frameworks mef managed extensible


    【解决方案1】:

    我遇到了类似的问题 - 将发现的插件复制到应用程序的目录后,即使在 DirectoryCatalog 上调用 .refresh() 后,DirectoryCatalog 也看不到它们。

    我发现单步执行代码解决了这个问题——我最好的猜测是,文件系统在 FileSystemWatcher 启动它的通知之后仍然需要片刻,MEF 才能扫描新程序集(可能完成一些晦涩的复制操作)并查看里面的零件。

    System.Threading.Thread.Sleep(1000),虽然很蹩脚,但解决了这个问题。

    【讨论】:

      【解决方案2】:

      我相信 MEF 只能加载同一程序集的一个版本(虽然我在 Silverlight 上尝试过)

      【讨论】:

        【解决方案3】:

        没有重新组合的触发器,因为您的目录实现不提供通知。实施INotifyComposablePartCatalogChanged 来解决这个问题。

        【讨论】:

        • 感谢您的回复,但我不明白这对我有什么帮助。在玩了一些 MEF 之后,似乎我可以将我的目录实现更改为基本上是一个存储类并摆脱 ComposablePartCatalog,因为我已经有一个 directoryCatalog。 DirectoryCatalog 确实实现了 INotifyComposablePartCatalogChanged,在我看来,这意味着如果我将 main 中的代码更改为 catalog.Catalogs.Add(pluginCatalog.directoryCatalog);,它应该会正确更新内容。
        • @mybrokengnome:如果您将pluginCatalog.directoryCatalog 更改为新容器,MEF 容器不会神奇地注意到。它仍然会收听旧版本的更改通知。在我看来,更改通知与您的问题非常相关;怎么可能?
        • 我没有在case之后改变它,它现在总是指向pluginCatalog.directoryCatalog(一个真正的目录目录,而不是我创建的目录)。由于 DC 实现了 INotifyComposablePartCatalogChanged,我对为什么 DC 没有发现新文件感到困惑,即使在我使用新路径创建新 DC 之后也是如此。我知道更改通知是相关的,但是 A)使用我当前的实现(我刚刚描述的)然后更改通知是通过 MEF DC 实现的,B)是否有任何示例说明如何实现您自己的通知侦听器会这样工作吗?
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-11-05
        • 1970-01-01
        • 2014-06-19
        • 1970-01-01
        • 2014-07-06
        • 1970-01-01
        相关资源
        最近更新 更多