2010-04-12 55 views
4

我在MEF Codeplex论坛上已经提出这个问题,但我还没有得到答复,所以我想我会尝试StackOverflow。这里是原来的职位,如果任何人的兴趣(这只是一个从它复印件):MEF +插件没有更新

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

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

感谢您的帮助,您都可以提供,它的驾驶我疯狂不能够想出解决办法“。

回答

3

没有触发重新组合,因为你的目录下,执行不提供通知。实施INotifyComposablePartCatalogChanged到解决这个问题。

+0

感谢您的回复,但我不明白这对我有何帮助。在更多地使用MEF之后,似乎我可以将我的目录实现更改为基本的存储类,并且摆脱ComposablePartCatalog,因为我已经有了一个directoryCatalog。 DirectoryCatalog确实实现了INotifyComposablePartCatalogChanged,这对我来说意味着它应该正确地更新东西,如果我将主代码更改为catalog.Catalogs.Add(pluginCatalog.directoryCatalog);. – user64718 2010-04-13 03:13:24

+0

@mybrokengnome:如果将'pluginCatalog.directoryCatalog'更改为新的,MEF容器不会奇迹般地注意到。它仍然会听取更改旧通知的通知。在我看来,变更通知与您的问题非常相关;它怎么会不是? – 2010-04-13 08:54:49

+0

我没有改变它的情况下,它现在总是指向pluginCatalog.directoryCatalog(一个真实的目录目录,而不是我创建的目录)。由于DC实现INotifyComposablePartCatalogChanged我很困惑,为什么DC没有发现新文件,即使我用新路径创建一个新DC。我得到的变化通知是相关的,但A)与我目前的实现(我刚刚描述),然后改变通知是通过MEF DC和B)实现有什么地方做什么来实现你自己的通知监听器会这样工作吗? – user64718 2010-04-13 13:46:59

0

我相信MEF只能加载相同组件的一个版本(我想在Silverlight中虽然)

1

我有一个类似的发出─复制发现插件应用程序的目录后,一DirectoryCatalog止跌即使在打电话给.refresh()之后,他们也不会看到他们e DirectoryCatalog。

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

System.Threading.Thread.Sleep(1000),跛脚,解决了这个问题。