我正在运行一个Owin Self托管网络Api,其典型控制器如下:
[HttpGet]
[Route("refresh/{secId}")]
[ResponseType(typeof(int))]
public async Task<IHttpActionResult> RefreshElement(int secId)
{
var count = await _db.Refresh(secId);
if (count == 0)
return NotFound();
return Ok(count);
}
假设_db.Refresh()长时间运行(几秒),有时会引发异常。
我设法重现的问题是:
不再等待_db.Refresh的结果 - 因为我看到它何时返回异常,当任务为GCd时,它会通过TPL未观察到的异常处理显示...
也许是因为这种互动,.net团队改变了未处理的异常政策,不是为了拆除流程(我认为是4.5以后)......那么这个问题的优秀模式是什么?特别是对于使用OWIN的自托管WebApi - 因为我仍然将未观察到的异常记录为FATAL:)
我可以让_db.Refresh()接受一个取消令牌,但是我如何/何时在自我主机webapi中设置取消令牌以取消连接/取消?
答案 0 :(得分:13)
也许这并不能完全回答你的问题,但我相信它会回答这最后一部分:
我可以让_db.Refresh()接受一个取消令牌,但是我如何/何时在自我主机webapi中设置取消令牌以取消连接/取消?
如果在操作方法中添加CancellationToken
参数,框架会在调用时提供一个参数。
[HttpGet]
[Route("refresh/{secId}")]
[ResponseType(typeof(int))]
public async Task<IHttpActionResult> RefreshElement(
int secId, CancellationToken cancellationToken)
{
var count = await _db.Refresh(secId, cancellationToken);
if (count == 0)
return NotFound();
return Ok(count);
}
但是请注意,在操作方法退出之前,客户端可能会断开一个小的机会窗口,在这种情况下,您的TPL未观察到的异常仍将发生。
答案 1 :(得分:0)
您可以通过自己实施中间件来处理请求超时。
IOwinContext
有一个Request
属性,该属性本身包含名为CancellationToken
的{{1}},可以轮询该请求以查看请求是否已取消:
CallCancelled
注意一般的异常条款是因为我不确定OWIN会在tge请求被取消时抛出TCE,所以无论如何都要确定令牌。
修改强>
正如@usr向我指出的那样,这个解决方案将处理外部public class CancellationAwareOwinMiddleware : OwinMiddleware
{
public async Task Invoke(IOwinContext owinContext)
{
try
{
await Next.Invoke(owinContext);
}
catch (TaskCanceledException)
{
// Handle cancellation.
}
catch (Exception e)
{
if (owinContext.Request.CallCancelled.IsCancellationRequested)
{
// Handle cancellation.
}
}
}
}
未被观察到的情况,这是我正在做的一个相当大的假设。如果这是内部任务,我需要重新考虑解决方案。