December 12, 2019

Custom Exception throws as Aggregate Exception

I faced this issue while using Custom Exception class with Task Parallel Library. The issue was that when I throw the Custom Exception, it was not getting caught in Custom Exception catch clause. In this post I will explain how you can catch Custom Exception when working with TPL.

Lets start with example code, here is the definition for my Custom Exception class.

class MyCustomException : Exception
{
 //custom code...
}

Here is the function GetOrderCount() with is throwing this Custom Exception from new Task.

public static class UserManager
{
 public static int GetOrderCount(string userName)
 {
  int orderCount = Task.Run(() => {
   if (userName == "test") // any condition, to validate and throw exception...
   {
    throw new MyCustomException();
   }

   return 10; // get value from database...
  }).Result;

  return orderCount;
 }
}

Here is the Controller's action method which is calling previous method GetOrderCount().

[HttpGet("{userName}")]
public IActionResult GetOrderCount(string userName)
{
 try
 {
  int count = UserManager.GetOrderCount(userName);
  
        return Ok(new { OrderCount = count });
 }
 catch (MyCustomException ex1) // this will get ignored
 { 
  //handle for MyCustomException  
 } 
 catch (Exception ex3) // this will handle any exception, including MyCustomException
 {
  //handle for generic exception
 }
}

Here is the issue, I was expecting to run code for MyCustomException handle but it was not getting there. After searching on this, I found that this is the behavior designed because of TPL. If a child task throws an exception, that exception is wrapped in an AggregateException exception before it is propagated to the parent. And chain cycle continues if the parent itself is a child task of some other parent task, i.e. which also wraps its own AggregateException exception before it propagates it back to the calling thread.

In a nutshell, MyCustomException is actually wraps in AggregateException, so I have to place a catch for AggregateException instead of MyCustomException. And to get access MyCustomException object, I have to read the InnerException property of AggregateException.

Here is the updated code after making this change.

[HttpGet("{userName}")]
public IActionResult GetOrderCount(string userName)
{
 try
 {
  int count = UserManager.GetOrderCount(userName);
  
        return Ok(new { OrderCount = count });
 }
 catch (AggregateException ex1) // now this will handle MyCustomException 
 { 
        MyCustomException myException = ex1.InnerException as MyCustomException;
  //other handling logic for MyCustomException...
 } 
 catch (Exception ex3) // this will handle any other exception
 {
  //handle for generic exception
 }
}

This way you can get handle of your Custom Exception.

References:

No comments:

Post a Comment