Monday, December 19, 2016

Preserve Stacktrace in C# without reflection: ExceptionDispatchInfo Class

Exception handling is a basic build block of any application. To identify the actual problem through exception handling, stack trace play an important role.
Stack-trace contains information about method call hierarchy from beginning to actual point of exception. But sometimes exception handled in wrong way and application loose actual stack trace.
Prior to .net framework 4.5, developer has to perform reflection to preserve stack trace by calling "InternalPreserveStackTrace()" internal method. But this solution is not easy and can degrade the application performance.
.Net framework 4.5 introduces a new class ExceptionDispatchInfo. This class provide method ExceptionDispatchInfo.Capture(Exception) to take the snapshot of exception at any point of time and preserve the all exception information. These snap-shot can be re-throw at any future point of time using ExceptionDispatchInfo.Throw Method (). Throw method preserves the stack-trace and other exception information. This method can be useful in some scenarios mention scenarios.
  1. In multi-threaded application, capture the exception in child thread and raise it in main/parent thread.
var exceptionList = new BlockingCollection<ExceptionDispatchInfo>();
ThreadPool.QueueUserWorkItem(()=>
{
    try
    {
        // Code that may raise exception.
    }
    catch (Exception ex)
    {
        ExceptionDispatchInfo exceptionInfo = ExceptionDispatchInfo.Capture(ex);
        exceptionList.Add(exceptionInfo);
    }
    exceptionList.CompleteAdding();
});

foreach (ExceptionDispatchInfo exceptionInfo in exceptionList.GetConsumingEnumerable())
{
    try
    {
      exceptionInfo.Throw();
    }
    catch (Exception ex)
    {
        Console.WriteLine("{0}", ex);
    }
}


2. Perform some operations to make application stable before raise exception with original stack trace.
private ExceptionDispatchInfo _exceptionInfo=null;

private void MainMethod(){
     ExceptionMethod();
     PerformCleanUpOpsAndRaiseException();
}

private static void ExceptionMethod(){
        try
 {
  // Code that can generate exceptions here
 }
 catch (Exception ex)
 {
  // Perform some operation or Do some logging or whatever
  // Capture snap-shot of exception 'ex'.
  _exceptionInfo = ExceptionDispatchInfo.Capture(ex);
 }
}

private void PerformCleanUpOpsAndRaiseException(){
  // Code that needs to be execute before throwing an exception.
  if(_exceptionInfo!=null){
     // Raise captured exception if any
  _exceptionInfo.Throw();
  }
}

No comments:

Post a Comment