Let's say I have a Web API service that calls my user Service to return user profile information etc..
UserProfileService
can throw UserNotFoundException
. When thrown, it is serialized and sent as an inner exception in an AggregateException
which can be caught in the calling method. This service uses Service Fabric's service remoting for RPCing.
My WebAPI is calling my service like this:
[HttpGet]
public async Task<IActionResult> Get(int id)
{
try
{
var profile = await _userService.GetProfileAsync(int id);
return Json(profile);
} catch (AggregateException ae)
{
// Here I want to call NotFound() if `UserNotFoundException`
// was thrown, otherwise...
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
So a couple of questions here:
Naively I'd do something like this:
try { /* ... */ } catch (AggregateException ae)
{
foreach(var e in ae.InnerExceptions)
{
if (e is UserNotFoundException)
{
return NotFound();
}
}
return errorResponse ?? StatusCode(StatusCodes.Status500InternalServerError);
}
But the trouble with this is, if there are multiple exceptions only one will "win". And, I believe - although there is no guarantee, that the earliest added Exception
s will have priority, as they'll have a lower index in InnerExceptions
. Am I over thinking this, and would this solution be fine? The only time my custom exceptions will be thrown are when I know they should be thrown, surely?
This leads me to my other question:
AggregateException
.Is it when you have Task a
calling Task b
calling a Task c
, c
throws, b
doesn't throw, a
throws, you'd get aggregate exception containing a
and c
's exceptions?
I'll answer your questions backwards:
2) AggregateException
has a contructor that allows an IEnumerable<Exception>
as parameter. This is how it can contain multiple inner exceptions. This means that your aggregate exception won't contain more than one inner exception unless you explicitly throw an AggregateException
with multiple inner exceptions. Say you have a Task a
calling Task b
calling Task c
. If c
throws an exception, which is not caught in a
or b
, a
would throw an AggregateException
with an inner AggregateException
with an inner exception thrown by c
.
1) Your example works just fine. If you want it a bit shorter you could catch it by inner exception:
try
{
// ...
}
catch (AggregateException ex) when (ex.InnerException is UserNotFoundException)
{
// ...
}
catch (AggregateException ex) when (ex.InnerException is SomeOtherException)
{
// ...
}
You could also catch them with some if-statements like you did in your example:
try
{
// ...
}
catch (AggregateException ex)
{
if (ex.InnerException is UserNotFoundException)
{
// ...
}
else if (ex.InnerException is SomeOtherExeption)
{
// ...
}
}