Pregunta

Tengo muchos métodos que llama mediante Delegate.DynamicInvoke. Algunos de estos métodos hacen las llamadas bases de datos y me gustaría tener la capacidad de coger un SqlException y no coger el TargetInvocationException y cazar a través de sus ampollas de encontrar lo que está realmente mal se ha ido.

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

El método PreserveStackTrace 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(); 
}
¿Fue útil?

Solución

If you just want to re-throw an inner exception preserving its stack trace, you can do it with a method like this:

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

This technique is used by Rx (and is exposed by them as an extension method Exception.PrepareForRethrow) and is also used by the Async CTP by its automatic-unwrapping system (without a publicly-exposed API).

Note, however, that this technique is technically unsupported. Hopefully Microsoft will add an official API for this in the future. A suggestion has been opened on Microsoft Connect if you would like to vote for it.

Update: An official API has been added to .NET 4.5: ExceptionDispatchInfo.

Otros consejos

You need to keep in mind why .NET wraps the exception with a TargetInvocationException instead of just letting the original exception through. There's a really good reason for that, it isn't obvious where the real reason for the exception came from. Was it because the DynamicInvoke() call is borked? Not unlikely, there's nothing that the compiler can do to ensure that the correct arguments were passed. Or did the invoked target method throw all by itself?

You need to know both to judge the real reason for the exception. Intentionally hiding the TargetInvocationException is going to give you a hard time to diagnose the source of the trouble if it was indeed a problem with the DynamicInvoke() call. Avoid doing this.

IIRC it is not possible to preserve exception completely, however stack trace can be preserved with some reflection. Here is blog post describing how to do it: http://iridescence.no/post/Preserving-Stack-Traces-When-Re-Throwing-Inner-Exceptions.aspx

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top