代码之家  ›  专栏  ›  技术社区  ›  user64718

MEF+插件未更新

  •  4
  • user64718  · 技术社区  · 14 年前

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

    MEF Codeplex

    “首先让我说,我对MEF(今天才发现它)是完全陌生的,到目前为止对它非常满意。然而,我遇到了一个非常令人沮丧的问题。我正在创建一个应用程序,将有一个插件架构和插件将只存储在一个单独的DLL文件(或编码到主应用程序)。DLL文件需要能够在运行时重新编译,应用程序应该能够识别这一点并重新加载插件(我知道这很困难,但这是一个要求)。为了达到这个目的,我采取了 http://blog.maartenballiauw.be/category/MEF.aspx 在那里(查找WebServerDirectoryCatalog)。基本上,我们的想法是“监视plugins文件夹,将新的/修改过的程序集复制到web applications/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中,以便我可以修改plugins文件夹中的DLL)。 所以现在我应该可以重新编译插件DLL,把它放到插件文件夹中,我的文件观察程序检测到它已经更改,然后将它复制到文件夹“2”,directoryCatalog应该指向新文件夹。所有这些 真的有用!问题是,尽管似乎所有东西都指向了正确的位置,但我的IEnumerable变量从未用新插件更新过。如此接近,却又如此遥远!有什么建议吗? 我知道这样做的缺点,没有dll被卸载并导致内存泄漏,但它是一个Windows应用程序,可能每天至少会启动一次,而且插件不太可能改变 通常是这样,但客户端仍然要求它在不重新加载应用程序的情况下执行此操作。谢谢!

    谢谢你们能提供的帮助,我都快疯了,弄不明白这一点。”

    3 回复  |  直到 14 年前
        1
  •  3
  •   Wim Coenen    14 年前

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

        2
  •  1
  •   Clems    14 年前

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

        3
  •  1
  •   Chris    13 年前

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

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

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