2010-10-26 20 views
19

Scenario: utilizzo Managed Extensibility Framework per caricare plug-in (esportazioni) in fase di esecuzione in base a un contratto di interfaccia definito in una dll separata . Nella mia soluzione di Visual Studio, ho 3 diversi progetti: l'applicazione host, una libreria di classi (che definisce l'interfaccia - "IPlugin") e un'altra libreria di classi che implementa l'interfaccia (l'esportazione - "MyPlugin.dll").MEF: "Impossibile caricare uno o più dei tipi richiesti Recupero di LoaderExceptions per ulteriori informazioni"

Il padrone di casa si presenta per le esportazioni nella propria directory principale, quindi durante i test, ho costruire l'intera soluzione e copiare Plugin.dll dalla cartella libreria di classi Plugin bin/release nella directory di debug del padrone di casa in modo che DirectoryCatalog dell'host troverà ed essere in grado di aggiungerlo al CompositionContainer. Plugin.dll non viene copiato automaticamente dopo ogni ricostruzione, quindi lo faccio manualmente ogni volta che ho apportato modifiche al contratto/all'implementazione.

Tuttavia, un paio di volte ho eseguire l'applicazione host senza aver copiato (un aggiornamento) Plugin.dll prima, e ha generato un'eccezione durante la composizione:

Unable to load one or more of the requested types. Retrieve the LoaderExceptions for more information

Questo è di Naturalmente a causa del fatto che il Plugin.dll sta cercando di importare da implementa una versione diversa di IPlugin, dove le proprietà/le firme del metodo non corrispondono. Sebbene sia facile evitarlo in un ambiente controllato e monitorato, evitando semplicemente (duh) implementazioni IPlugin obsolete nella cartella dei plugin, non posso fare affidamento su tali presupposti nell'ambiente di produzione, in cui è possibile incontrare plug-in legacy.

Il problema è che questa eccezione blocca efficacemente l'intera azione Compose e le esportazioni no vengono importate. Avrei preferito che le mismatching delle implementazioni IPlugin venissero semplicemente ignorate, in modo che altre esportazioni nel/sui catalogo/i, implementando la versione corretta di IPlugin, siano ancora importate.

C'è un modo per realizzare questo? Sto pensando una delle diverse possibili opzioni:

  • C'è una bandiera per impostare sul CompositionContainer ("ignora in mancanza di importazioni") prima o al momento della chiamata Componi
  • C'è una bandiera simile per specificare il <ImportMany()> attributo
  • C'è un modo per "agganciare" al processo di iterazione sottostante Compose(), ed essere in grado di trattare con ciascuno (fallito) importazione individualmente
  • Uso nome sicuro firma per cercare in qualche modo unicamente per le importazioni attuano il corrente versione di IPlugin

Idee?

risposta

15

Ho anche eseguito a similar problem.

Se si è certi di voler ignorare tali assembly "cattivi", la soluzione è chiamare AssemblyCatalog.Parts.ToArray() subito dopo aver creato ciascun catalogo di assiemi. Questo attiverà il ReflectionTypeLoadException che menzioni. Hai quindi la possibilità di intercettare l'eccezione e ignorare l'assemblea errata.

Dopo aver creato AssemblyCatalog oggetti per tutti i gruppi di "buoni", è possibile aggregare in un AggregateCatalog e passare che al costruttore CompositionContainer.

+0

I'll dare un'occhiata. E sì, sono sicuro di volerli ignorare - non posso comunque utilizzarli, e fanno casino per tutti quelli che voglio, quindi è un gioco da ragazzi, no? :) – d7samurai

+0

@ d7samurai: alcune persone finiranno qui cercando su google il messaggio di eccezione. Volevo solo sottolineare per loro che questo non risolve la causa sottostante dell'errore. Inoltre, l'introduzione di un errore non presidiato (noto anche come comportamento "riprendi l'errore successivo") non è esattamente un gioco da ragazzi; può complicare enormemente il rilevamento e la diagnosi dei bug. Consiglio di dare almeno qualche indicazione all'utente che è successo qualcosa di brutto. –

+0

Naturalmente. "Ignorando" intendo "andare avanti senza lasciarlo fermare l'intero processo di importazione". Non esiste un utente, poiché si tratta di un servizio di Windows, ma tutto ciò che fa (bootstrapper) viene registrato, sia le normali operazioni che le eccezioni (incluso il dumping di tutte le eccezioni in "LoaderExceptions" - quando applicabile). – d7samurai

7

Questo problema può essere causato da diversi fattori (eventuali eccezioni sugli assembly caricati), come l'eccezione, dice, cerca nella ExceptionLoader per (si spera) avere qualche idea

Un altro problema/soluzione che ho trovato, è quando si utilizza DirectoryCatalog, se non si specifica il secondo parametro "searchPattern", MEF caricherà TUTTE le DLL in tale cartella (incluse terze parti) e inizierà a cercare i tipi di esportazione, che possono anche causare questo problema, una soluzione è avere un nome convenzione su tutti gli assembly che esportano i tipi e specificare che nel costruttore DirectoryCatalog, io uso * _Plugin.dll, in questo modo MEF caricherà solo gli assembly che contengono esportato tipi

Nel mio caso MEF stava caricando una dll NHibernate e gettando qualche errore di versione di assembly sul LoaderException (questo errore può accadere con qualsiasi delle DLL nella directory), questo approccio ha risolto il problema

7

Qui è un esempio dei metodi sopra menzionati:

var di = new DirectoryInfo(Server.MapPath("../../bin/")); 

     if (!di.Exists) throw new Exception("Folder not exists: " + di.FullName); 

     var dlls = di.GetFileSystemInfos("*.dll"); 
     AggregateCatalog agc = new AggregateCatalog(); 

     foreach (var fi in dlls) 
     { 
      try 
      { 
       var ac = new AssemblyCatalog(Assembly.LoadFile(fi.FullName)); 
       var parts = ac.Parts.ToArray(); // throws ReflectionTypeLoadException 
       agc.Catalogs.Add(ac); 
      } 
      catch (ReflectionTypeLoadException ex) 
      { 
       Elmah.ErrorSignal.FromCurrentContext().Raise(ex); 
      } 
     } 

     CompositionContainer cc = new CompositionContainer(agc); 

     _providers = cc.GetExports<IDataExchangeProvider>(); 
+0

Questo non funziona per ASP.NET MVC se non si utilizza un dominio separato, perché Assembly.LoadFile carica il file in un dominio diverso. Per risolverlo devi solo usare Assembly.LoadFrom. – Eben

Problemi correlati