2010-11-10 14 views
22

Estoy usando un DirectoryCatalog en MEF para satisfacer las importaciones en mi aplicación. Sin embargo, a veces hay conjuntos ofuscados en el directorio que causan un ReflectionTypeLoadException cuando intento componer el catálogo.Manejar ReflectionTypeLoadException durante la composición de MEF

Sé que puedo evitarlo usando un directorio separado o usando un filtro de búsqueda en el DirectoryCatalog pero quiero una forma más general de resolver el problema. ¿Hay alguna forma en que pueda manejar la excepción y permitir que la composición continúe? ¿O hay otra solución más general?

Respuesta

25

DirectoryCatalog ya tiene el código para capturar ReflectionTypeLoadException e ignorar esos ensamblajes. Lamentablemente, como tengo reported, simplemente crear el AssemblyCatalog aún no activará la excepción para que el código no funcione.

La excepción se desencadena en realidad por la primera llamada al AssemblyCatalog.Parts.

lugar de utilizar el DirectoryCatalog del MEF, que tendrá que hacerlo usted mismo:

  • escanear un directorio para montajes
  • carga de cada conjunto y crea una AssemblyCatalog para ello
  • invocar AssemblyCatalog.Parts.ToArray() para forzar la excepción, y atrapalo
  • agrega todos los buenos catálogos con un AggregateCatalog
+1

¡Excelente! Gracias. Creé un SafeDirectoryCatalog derivado de AggregateCatalog y lo usé para cargar un archivo a la vez, y lo agregué a la colección de catálogos solo si no se ahogó al acceder a Parts. ¡Funciona bien! –

+1

@PhilJPearson: He actualizado mi respuesta para .NET 4.5. Aparentemente, llamar '.Parts' ya no es suficiente, ahora debes forzar la enumeración con' .Parts.ToArray() '. –

+2

por lo que no han corregido el error en 4.5. ¡En realidad lo han empeorado! –

43

para salvar a otros de escribir su propia implementación de la SafeDirectoryCatalog, aquí es el que me ocurrió con base en las sugerencias de 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 pero agregaría algo de registro en la declaración catch. También es posible que desee atrapar 'BadImageFormatException' para ignorar las DLL que no son de .NET. –

+0

Directory.EnumerateFiles debe ser Directory.GetFiles –

+4

Steve, Directory.EnumerateFiles es nuevo en .NET 4 y tiene beneficios de rendimiento en cuanto a que las entradas se devuelven cuando las solicita, en lugar de todas a la vez como con.GetFiles –

2

que estaba haciendo esto desde una API que estaba escribiendo y el SafeDirectoryCatalog haría no registrar múltiples exportaciones que coincidan con una sola importación desde diferentes ensamblajes. La depuración de MEF normalmente se realiza a través de depurador y TraceListener. Ya utilicé Log4Net y no quería que alguien necesitara agregar otra entrada al archivo de configuración solo para admitir el registro. http://blogs.msdn.com/b/dsplaisted/archive/2010/07/13/how-to-debug-and-diagnose-mef-failures.aspx Se me ocurrió:

// 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

cualquier código de muestra completo? Gracias – Kiquenet

Cuestiones relacionadas