Friday, January 28, 2011

Quickie: Unwrapping Invocation Exceptions

If you work with dynamic invocation in any way, shape or form you're bound to catch a TargetInvocationException at some time or another. However, the fact that the real exception has been wrapped into a TargetInvocationException can hide a multitude of sins - potentially all sorts of different exception types can get wrapped from all sorts of sources.

One further problem is that it's entirely possible to get nested TargetInvocationExeptions as the call stack unwinds - again masking what the real problem is. This was the precise situation I found myself in with my nicely crafted n-tier WCF-based system.

So to make life just a little easier on myself when dealing with the exceptions thrown whilst performing a dynamic invocation I put together the following extension methods - one that re-wraps an exception and one that un-wraps nested TargetInvocationExceptions.

To use it, we just catch and re-throw, except using the extension methods.
try
{
// Do something dangerous with dynamic invocation
}
catch (TargetInvocationException ex)
{
throw ex.UnwrapTargetInvocationExceptions().ForRethrowing();
}
What you'll get is an exception throw of the same type as the actual fault, regardless of how many nested TargetInvocationExceptions it is wrapped in. Because we're nicely wrapping the exception we also maintain the call stacks to let you trace what's really going on.

Finally, here's the source - use it at your own risk - I've found it handy!

public static class ExceptionExtensions
{
///
/// Returns an exception wrapping an exception, but of the same type a
/// that's exception's INNER exception.
///

///
/// This allows us to write code like
/// throw ex.InnerException.ForRethrowing()
/// to re-throw an inner exception type without losing the stack trace.
///
public static T ForRethrowing(
this T innerException, Exception outerException)
where T: Exception
{
var actualType = innerException.GetType();

var ctorParams = new object[] {
innerException.Message, outerException
};
var result = (T)Activator.CreateInstance(actualType, ctorParams);
return result;
}

///
/// Unwraps an exception from any wrapping target invocation exceptions.
///

public static Exception UnwrapTargetInvocationExceptions(
this Exception exception)
{
var innerException = exception.InnerException;

// not a TargetInvocationException, or no inner exception? Then
// just return the exception itself
if ((innerException == null) ||
(!(exception is TargetInvocationException)))
{
return exception;
}

return innerException.UnwrapTargetInvocationExceptions();
}
}

No comments: