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.