【发布时间】:2017-03-23 17:14:15
【问题描述】:
我正在.NET Core 中编写一个 ASP.NET MVC 站点。我正在尝试封装一些常见的异常处理。在基类中我有这个方法。
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();
}
}
}
从继承自该基类的控制器中,我使用该方法如下:
[Route("api/[controller]")]
public class MyController : BaseController<MyController>
{
[HttpGet]
public IActionResult Get()
{
return ExceptionHandledOperation(() => Ok(_someService.GetAsync().Result),
NotFound);
}
}
假设 _someService.GetAsync() 方法是这样的:
public class SomeService
{
public async Task<PreconfigurationData> GetAsync()
{
// http request removed for brevity
if (!response.IsSuccessStatusCode)
throw new Exception("SomeService Exception");
}
}
这很好,可以在基类方法中捕获我的异常并返回 NotFound 结果。
但是,我想避免从 SomeService.GetAsync 方法调用 .Result。我读到的所有地方都说不要这样做,因为它会死锁。
所以我将我的基本控制器修改为:
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());
}
}
}
MyController 是这样的:
[Route("api/[controller]")]
public class MyController : BaseController<MyController>
{
[HttpGet]
public async Task<IActionResult> Get()
{
return await ExceptionHandledOperationAsync(() => Ok(_someService.GetAsync()),
NotFound);
}
}
但是,我的 SomeService.GetAsync 方法引发的异常从未被捕获,并且我从未收到我打算在处理异常时发送的 NotFound 响应。
我读到的每一个地方都说你只需要在 try 中等待任务,然后任何异常都会被捕获,但我的从来没有。
已解决
我终于能够解决这个问题。感谢曾先生的帮助。
【问题讨论】:
-
不要在 ASP.NET Core 应用程序中调用
Task.Run。您不会从中获得任何好处,甚至可能会降低您的性能,并且您可能会弄乱 ThreadPool 管理。仅使用真正运行异步的 await/async 操作(文件 IO、网络、数据库访问等) -
msdn.microsoft.com/en-us/magazine/dn802603.aspx 是关于 ASP.NET 但仍然适用于 ASP.NET Core
-
@Tseng 我不明白如何在不使用 Task.Run 的情况下使 operation.Invoke() 异步。没有操作。InvokeAsync()。我尝试使用 TaskFactory.FromAsync(operation.BeginInvoke, operation.EndInvoke, null),但这引发了异常。
-
除非您调用的操作已经是异步且真正的异步操作(读取文件、执行网络请求或数据库请求等)。只需同步运行它。在 ASP.NET Core 中异步运行同步操作没有任何好处,它甚至可能会损害您的性能。这与桌面开发不同,您使用它在后台运行东西而不锁定 UI 线程
-
否则要在回调中运行异步代码,将其转为返回任务:
Func<Task<IActionResult>,然后您可以执行类似的操作,然后执行类似的操作:() => await _someService.GetAsync();。和return await handleException.Invoke();
标签: asynchronous exception-handling asp.net-core async-await task