I'm writing a ASP.NET MVC site in .NET Core. I'm trying to encapsulate some common exception handling. In a Base class I have this method.
public abstract class BaseController<TController> : Controller where TController : Controller
{
protected IActionResult ExceptionHandledOperation(Func<IActionResult> operation, Func<IActionResult> handleException)
{
try
{
return operation.Invoke();
}
catch (Exception exception)
{
Logger.LogError($"Operation {Request.Path} Exception", exception);
return handleException.Invoke();
}
}
}
From a controller that inherits from this base class, I utilize this method like so:
[Route("api/[controller]")]
public class MyController : BaseController<MyController>
{
[HttpGet]
public IActionResult Get()
{
return ExceptionHandledOperation(() => Ok(_someService.GetAsync().Result),
NotFound);
}
}
Assume the _someService.GetAsync() method is this:
public class SomeService
{
public async Task<PreconfigurationData> GetAsync()
{
// http request removed for brevity
if (!response.IsSuccessStatusCode)
throw new Exception("SomeService Exception");
}
}
This worked fine and would catch my exception in the base class method and return the NotFound result.
However, I wanted to avoid calling .Result from the SomeService.GetAsync method. Everywhere I read it says not to do that as it can deadlock.
So I modified my base controller to this:
public abstract class BaseController<TController> : Controller where TController : Controller
{
protected async Task<IActionResult> ExceptionHandledOperationAsync(Func<IActionResult> operation, Func<IActionResult> handleException)
{
try
{
return await Task.Run(() => operation.Invoke());
}
catch (Exception exception)
{
Logger.LogError($"Operation {Request.Path} Exception", exception);
return await Task.Run(() => handleException.Invoke());
}
}
}
And MyController like this:
[Route("api/[controller]")]
public class MyController : BaseController<MyController>
{
[HttpGet]
public async Task<IActionResult> Get()
{
return await ExceptionHandledOperationAsync(() => Ok(_someService.GetAsync()),
NotFound);
}
}
However, my exception thrown from my SomeService.GetAsync method is never caught and I never get the NotFound response I intend to send when handling the exception.
Everywhere I read it says you just need to await the task in the try and then any exceptions will be caught, but mine never does.
Solved
I was finally able to get this resolved. Thanks to Tseng for the help.
Task.Run
in ASP.NET Core application. You won't have a single benefit from it and may even reduce your performance and you may mess up with the ThreadPool management. Use await/async only operation which really run async (File IO, Network, Database access, etc.) – TsengFunc<Task<IActionResult>
, then you can do something like then do something like:() => await _someService.GetAsync();
. andreturn await handleException.Invoke();
– Tseng