MEF + 插件未更新

4

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

MEF Codeplex

“首先,我完全是新手(今天才发现)MEF,并且迄今为止非常满意。然而,我遇到了一个非常令人沮丧的问题。我正在创建一个具有插件架构的应用程序,插件将仅存储在单个DLL文件中(或编码到主应用程序中)。DLL文件需要能够在运行时重新编译,应用程序应该识别此并重新加载插件(我知道这很困难,但这是一个要求)。为了实现这一点,我采取了这里介绍的方法(查找WebServerDirectoryCatalog)。基本上,想法是“监视插件文件夹,将新的/修改的程序集复制到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应用程序,可能每天至少启动一次,并且插件不太可能经常更改,但客户要求它在不重新加载应用程序的情况下执行此操作。 谢谢!感谢您提供的任何帮助,我因无法弄清楚而感到疯狂。
3个回答

3

谢谢您的回复,但我不明白那如何帮助我。在进一步尝试MEF后,似乎我可以将我的目录实现更改为基本的存储类,并且摆脱ComposablePartCatalog,因为我已经有一个directoryCatalog在其中。DirectoryCatalog确实实现了INotifyComposablePartCatalogChanged,这意味着如果我在主代码中更改了catalog.Catalogs.Add(pluginCatalog.directoryCatalog);,它应该正确更新事物。 - user64718
@mybrokengnome:MEF容器不会神奇地注意到您是否将pluginCatalog.directoryCatalog更改为新目录。它仍将监听旧目录的更改通知。对我而言,更改通知与您的问题非常相关;它怎么可能不是? - Wim Coenen
我在案例之后没有更改它,现在它总是指向pluginCatalog.directoryCatalog(一个真正的目录目录,而不是我创建的目录)。由于DC实现了INotifyComposablePartCatalogChanged,我对于为什么DC没有发现新文件感到困惑,即使我使用新路径创建了一个新的DC。我知道更改通知很重要,但A)通过MEF DC实现更改通知,B)是否有任何示例可以说明如何实现自己的通知侦听器,以使其像这样工作? - user64718

1

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


1

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

我发现通过逐步执行代码解决了这个问题 - 我最好的猜测是,在 FileSystemWatcher 启动通知后,文件系统仍然需要一段时间才能扫描新的程序集(可能是为了完成某些晦涩的复制操作)并查看其中的部分,然后 MEF 才能扫描它们。

System.Threading.Thread.Sleep(1000)虽然不太好,但解决了这个问题。


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