20

Tengo muchos métodos que están llamando usando Delegate.DynamicInvoke. Algunos de estos métodos hacen llamadas a la base de datos y me gustaría tener la capacidad de atrapar un SqlException y no atrapar el TargetInvocationException y buscar entre sus internos para encontrar lo que realmente salió mal.Cómo volver a lanzar la excepción interna de una TargetInvocationException sin perder el seguimiento de la pila

yo estaba usando este método para volver a lanzar pero despeja el seguimiento de la pila:

try 
{ 
     return myDelegate.DynamicInvoke(args); 
} 
catch(TargetInvocationException ex) 
{ 
    Func<TargetInvocationException, Exception> getInner = null; 
    getInner = 
     delegate(TargetInvocationException e) 
     { 
     if (e.InnerException is TargetInvocationException) 
      return getInner((TargetInvocationException) e.InnerException); 

     return e.InnerException; 
     }; 

    Exception inner = getInner(ex); 
    inner.PreserveStackTrace(); 
    throw inner; 
} 

PreserveStackTrace El método es un método de extensión hasta me fijo gracias a otro puesto (no sé lo que realmente hace) . Sin embargo, esto no parece conservar la traza ya sea:

public static void PreserveStackTrace(this Exception e) 
{ 
    var ctx = new StreamingContext(StreamingContextStates.CrossAppDomain); 
    var mgr = new ObjectManager(null, ctx); 
    var si = new SerializationInfo(e.GetType(), new FormatterConverter()); 

    e.GetObjectData(si, ctx); 
    mgr.RegisterObject(e, 1, si); 
    mgr.DoFixups(); 
} 

Respuesta

27

Si lo que desea es volver a lanzar una excepción interna que conserva su traza de la pila, puede hacerlo con un método como este:

public static void Rethrow(this Exception ex) 
{ 
    typeof(Exception).GetMethod("PrepForRemoting", 
     BindingFlags.NonPublic | BindingFlags.Instance) 
     .Invoke(ex, new object[0]); 
    throw ex; 
} 

Esta técnica es utilizada por Rx (y se expone por ellos como un método de extensión Exception.PrepareForRethrow) y también es utilizado por el Async CTP por su sistema de desenvolvimiento automático (sin una API expuesta públicamente).

Tenga en cuenta, sin embargo, que esta técnica es técnicamente incompatible. Esperemos que Microsoft agregue una API oficial para esto en el futuro. Una sugerencia has been opened en Microsoft Connect si desea votar por ella.

Actualización: Se ha agregado una API oficial a .NET 4.5: ExceptionDispatchInfo.

+0

Punto interesante a tener en cuenta: ExceptionDispatchInfo en 4.5 significa que ve pilas de llamadas diferentes desde Rx cuando está volviendo a lanzar excepciones dependiendo de si tiene como objetivo 4.0 o 4.5. Los OnErrors no administrados volverán a aparecer con el seguimiento de la pila original cuando se dirijan a 4.5, pero no a 4.0. –

2

Debe tener en cuenta por qué .NET ajusta la excepción con una TargetInvocationException en lugar de simplemente dejar pasar la excepción original. Hay una muy buena razón para eso, no es obvio de dónde viene la verdadera razón de la excepción. ¿Fue porque la llamada de DynamicInvoke() tiene borked? No es improbable, no hay nada que el compilador pueda hacer para garantizar que se pasen los argumentos correctos. ¿O el método del objetivo invocado se lanzó solo?

Necesita saber ambos para juzgar el motivo real de la excepción. La ocultación intencional de TargetInvocationException le dará un tiempo difícil para diagnosticar el origen del problema si fue realmente un problema con la llamada DynamicInvoke(). Evita hacer esto.

+1

Aprecio el razonamiento, pero hace que sea más difícil escribir código de consumo ya que la flexibilidad de atrapar explícitamente los errores específicos ha desaparecido. –

+1

La captura de excepciones en este escenario es extraordinariamente difícil de todos modos. No sabes nada sobre el objetivo delegado, no puedes adivinar cómo modificó el estado del programa. Y, por lo tanto, no puede restablecer el estado cuando maneja la excepción. –

+0

Un caso en el que confunde TIE es si usted tiene control sobre el código "interno" que arroja la excepción Y sobre el código externo que quiere manejar excepciones. Si el código interno se inyecta utilizando algo que lo envuelve en un proxy dinámico, ahora es más difícil captar las excepciones que arroja del código interno. Esto ocurre, por ejemplo, con los ORM donde algunas de las clases de entidades pueden estar envueltas, lo que preferiblemente no debería preocupar al código circundante. –

Cuestiones relacionadas