MEF +插件未更新

发布于 2024-08-28 18:55:33 字数 4660 浏览 12 评论 0原文

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

MEF Codeplex

“首先我要说的是,我对 MEF 完全陌生(今天才发现它),到目前为止我对它非常满意。但是,我遇到了一个非常令人沮丧的问题。我”我正在创建一个具有插件架构的应用程序,并且插件将仅存储在单个 DLL 文件中(或编码到主应用程序中)。DLL 文件需要能够在运行时重新编译,并且应用程序应该能够识别。并重新加载插件(我知道这很困难,但这是一个要求)。为了实现这一点,我采用了 http://blog.maartenballiauw.be/category/MEF.aspx(查找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 应用程序,可能每天至少启动一次,并且插件不太可能更改 经常如此,但客户端仍然要求在不重新加载应用程序的情况下执行此操作。谢谢!

感谢大家提供的帮助,无法解决这个问题让我发疯。”

I asked this on the MEF Codeplex forum already, but I haven't gotten a response yet, so I figured I'd try StackOverflow. Here's the original post if anyone's interested (this is just a copy from it):

MEF Codeplex

"Let me first say that I'm completely new to MEF (just discovered it today) and am very happy with it so far. However, I've ran in to a problem that is very frustrating. I'm creating an app that will have a plugin architecture and the plugins will only be stored in a single DLL file (or coded into the main app). The DLL file needs to be able to be recompiled during run-time and the app should recognize this and re-load the plugins (I know this is difficult, but it's a requirement). To accomplish this I took the approach covered http://blog.maartenballiauw.be/category/MEF.aspx there (look for WebServerDirectoryCatalog). Basically the idea is to "monitor the plugins folder, copy the new/modified assemblies to the web application’s /bin folder and instruct MEF to load its exports from there." This is my code, which is probably not the correct way to do it but it's what I found in some samples around the net:

        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);

and

    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));
    }

So all this actually works, and after the end of the code in main my IEnumerable variable is actually filled with all the plugins in the DLL (which if you follow the code is located in Plugins/1 so that I can modify the dll in the plugins folder).
So now at this point I should be able to re-compile the plugins DLL, drop it in to the Plugins folder, my FileWatcher detect that it's changed, and then copy it into folder "2" and directoryCatalog should point to the new folder. All this
actually works! The problem is, even though it seems like every thing is pointed to the right place, my IEnumerable variable is never updated with the new plugins. So close, but yet so far! Any suggestions?
I know the downsides of doing it this way, that no dll is actually getting unloaded and causing a memory leak, but it's a Windows App and will probably be started at least once a day, and the plugins are un-likely to change
that often, but it's still a requirement from the client that it does this without re-loading the app. Thanks!

Thanks for any help you all can provide, it's driving me crazy not being able to figure this out."

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

你怎么敢 2024-09-04 18:55:33

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

There is no trigger for recomposition, because your catalog implementation doesn't provide notifications. Implement INotifyComposablePartCatalogChanged to fix this.

风吹雨成花 2024-09-04 18:55:33

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

I believe MEF can only load one version of the same assembly (I was trying on Silverlight though)

深居我梦 2024-09-04 18:55:33

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

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

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

I was having a similar issue- after copying discovered Plugins to the application's directory, a DirectoryCatalog wouldn't see them, even after calling .refresh() on the DirectoryCatalog.

I found that stepping through the code resolved the issue- my best guess is that the filesystem still needs a moment after the FileSystemWatcher kicks off it's notification before MEF can scan the new assembly (perhaps to finish some obscure copy operation) and see the parts inside.

System.Threading.Thread.Sleep(1000), lame as it is, solved the issue.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文