2010-11-10 14 views
22

Sto utilizzando uno strumento DirectoryCatalog in MEF per soddisfare le importazioni nella mia applicazione. Tuttavia, nella directory ci sono talvolta degli assembly offuscati che causano uno ReflectionTypeLoadException quando provo a comporre il catalogo.Handle ReflectionTypeLoadException durante la composizione MEF

So che posso aggirarlo utilizzando una directory separata o utilizzando un filtro di ricerca su DirectoryCatalog ma voglio un modo più generale per risolvere il problema. C'è un modo per gestire l'eccezione e consentire alla composizione di continuare? O c'è un'altra soluzione più generale?

risposta

25

DirectoryCatalog ha già il codice per catturare ReflectionTypeLoadException e ignorare quegli assembly. Sfortunatamente, poiché ho reported, la semplice creazione di AssemblyCatalog non attiverà ancora l'eccezione in modo che il codice non funzioni.

L'eccezione è effettivamente attivata dalla prima chiamata a AssemblyCatalog.Parts.

Invece di utilizzare il DirectoryCatalog dal MEF, dovrete farlo da soli:

  • scansione una directory per le assemblee
  • carico ogni gruppo e crea un AssemblyCatalog per esso
  • invocare AssemblyCatalog.Parts.ToArray() per forza l'eccezione e prenderlo
  • aggregare tutti i buoni cataloghi con un AggregateCatalog
+1

Eccellente! Grazie. Ho creato un SafeDirectoryCatalog derivato da AggregateCatalog e l'ho usato per caricare un file alla volta, aggiungendolo alla raccolta Cataloghi solo se non soffoca all'accesso a Parts. Funziona a meraviglia! –

+1

@PhilJPearson: ho aggiornato la mia risposta per .NET 4.5. Apparentemente chiamare '.Parts' non è più sufficiente, ora devi forzare l'enumerazione con' .Parts.ToArray() '. –

+2

quindi non hanno corretto il bug in 4.5 - in realtà lo hanno peggiorato! –

43

per salvare gli altri dalla scrittura propria implementazione del SafeDirectoryCatalog, qui è quello che mi è venuta in base alle proposte di Wim Coenen:

public class SafeDirectoryCatalog : ComposablePartCatalog 
{ 
    private readonly AggregateCatalog _catalog; 

    public SafeDirectoryCatalog(string directory) 
    { 
     var files = Directory.EnumerateFiles(directory, "*.dll", SearchOption.AllDirectories); 

     _catalog = new AggregateCatalog(); 

     foreach (var file in files) 
     { 
      try 
      { 
       var asmCat = new AssemblyCatalog(file); 

       //Force MEF to load the plugin and figure out if there are any exports 
       // good assemblies will not throw the RTLE exception and can be added to the catalog 
       if (asmCat.Parts.ToList().Count > 0) 
        _catalog.Catalogs.Add(asmCat); 
      } 
      catch (ReflectionTypeLoadException) 
      { 
      } 
      catch (BadImageFormatException) 
      { 
      } 
     } 
    } 
    public override IQueryable<ComposablePartDefinition> Parts 
    { 
     get { return _catalog.Parts; } 
    } 
} 
+6

+1 ma aggiungerei l'accesso all'istruzione catch. Si potrebbe anche voler prendere 'BadImageFormatException' per ignorare DLL non-.net. –

+0

Directory.EnumerateFiles deve essere Directory.GetFiles –

+4

Steve, Directory.EnumerateFiles è nuovo in .NET 4 e offre vantaggi in termini di prestazioni in quanto le voci vengono restituite quando vengono richieste, anziché tutte contemporaneamente.GetFiles –

2

stavo facendo questo da un API che stavo scrivendo e la SafeDirectoryCatalog sarebbe non registrare più esportazioni corrispondenti a una singola importazione da diversi assembly. Il debug di MEF viene in genere eseguito tramite debugger e TraceListener. Ho già usato Log4Net e non volevo che qualcuno avesse bisogno di aggiungere un'altra voce al file di configurazione solo per supportare la registrazione. http://blogs.msdn.com/b/dsplaisted/archive/2010/07/13/how-to-debug-and-diagnose-mef-failures.aspx Mi sono inventato:

// I don't want people to have to add configuration information to get this logging. 
    // I know this brittle, but don't judge... please. It makes consuing the api so much 
    // easier. 
    private static void EnsureLog4NetListener() 
    { 
     try 
     { 
      Assembly compositionAssembly = Assembly.GetAssembly(typeof (CompositionContainer)); 
      Type compSource = compositionAssembly.GetType("System.ComponentModel.Composition.Diagnostics.CompositionTraceSource"); 

      PropertyInfo canWriteErrorProp = compSource.GetProperty("CanWriteError"); 
      canWriteErrorProp.GetGetMethod().Invoke(null, 
                BindingFlags.Public | BindingFlags.NonPublic | 
                BindingFlags.Static, null, null, 
                null); 

      Type traceSourceTraceWriterType = 
       compositionAssembly.GetType(
        "System.ComponentModel.Composition.Diagnostics.TraceSourceTraceWriter"); 

      TraceSource traceSource = (TraceSource)traceSourceTraceWriterType.GetField("Source", 
                      BindingFlags.Public | 
                      BindingFlags.NonPublic | 
                      BindingFlags.Static).GetValue(null); 

      traceSource.Listeners.Add(new Log4NetTraceListener(logger));     
     } 
     catch (Exception e) 
     { 
      logger.Value.Error("Cannot hook MEF compisition listener. Composition errors may be swallowed.", e); 
     } 
    } 
+0

qualsiasi codice completo di esempio? grazie – Kiquenet