How to rethrow the inner exception of a TargetInvocationException without losing the stack trace
Asked Answered
T

3

22

I have many methods which are calling using Delegate.DynamicInvoke. Some of these methods make database calls and I would like to have the ability to catch a SqlException and not catch the TargetInvocationException and hunt through its inners to find what's actually gone wrong.

I was using this method to rethrow but it clears the stack trace:

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

The PreserveStackTrace method is an extension method I fixed up thanks to another post (I don't know what it actually does). However, this doesn't appear to preserve the trace either:

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(); 
}
Thirtyone answered 29/12, 2010 at 15:55 Comment(1)
Possible duplicate of How to rethrow InnerException without losing stack trace in C#?Edinburgh
T
33

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.

Tranship answered 29/12, 2010 at 19:30 Comment(3)
Interesting point to note: ExceptionDispatchInfo in 4.5 means that you see different call stacks from Rx when it is rethrowing exceptions depending on whether you are targeting 4.0 or 4.5. Unhandled OnErrors will be rethrown with the original stack trace when targeting 4.5, but not 4.0.Resistless
Better solution is ExceptionDispatchInfo view the answer , not use PreserveStackTrace ? anyways, better use PrepForRemoting if not ExceptionDispatchInfo available ?Wingfooted
@Kiquenet: When I wrote this answer, ExceptionDispatchInfo didn't exist yet. When it came out, I updated this answer to mention that there is now an official solution.Tranship
C
2

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.

Cardinale answered 29/12, 2010 at 16:24 Comment(3)
I appreciate the reasoning but it does make it more difficult to write consuming code as the flexibility of explicitly catching specific errors is gone.Thirtyone
Catching exceptions in this scenario is extraordinary difficult anyway. You don't know anything about the delegate target, you cannot guess how it mutated the state of the program. And can thus not restore state when you handle the exception.Cardinale
One case where TIE confuses matter is if you have control over both the "inner" code that throws the exception AND the outer code that wants to handle exceptions. If the inner code is injected using something that wraps it in a dynamic proxy, it now becomes more difficult to catch the exceptions you throw yourself from the inner code. This happens for instance with ORMs where some of the entity classes may be wrapped, which preferably the surrounding code shouldn't need to care about.Lidda
G
0

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

Gravelblind answered 29/12, 2010 at 16:13 Comment(1)
webskaper.no/wst/post/… not foundWingfooted

© 2022 - 2024 — McMap. All rights reserved.