异步Web Api控制器 - 如何处理取消?

Viv*_*vek 8 c# asp.net-web-api owin

我正在运行Owin Self托管的Web 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);
    }
Run Code Online (Sandbox Code Playgroud)

假设_db.Refresh()长时间运行(几秒),有时会抛出异常.

我设法重现的问题是:

  • 请求命中方法,_db.Refresh被触发
  • 请求被取消(套接字关闭)

不再等待_db.Refresh的结果 - 因为我看到它何时返回异常,当任务是GCd时它会通过TPL未观察到的异常处理显示出来...

也许是因为这种互动,.net团队改变了未处理的异常政策而不是拆除流程(我认为是4.5以后)......那么这个问题的好处是什么?特别是对于使用OWIN的自托管WebApi - 因为我仍然将未观察到的异常记录为FATAL :)

我可以让_db.Refresh()接受一个取消令牌,但是我如何/何时在自我主机webapi中设置取消令牌以断开/取消?

Tim*_*lds 14

也许这并没有完全回答你的问题,但我相信它回答了最后一部分:

我可以让_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);
}
Run Code Online (Sandbox Code Playgroud)

但请注意,在操作方法退出之前,客户端可能仍然存在一个小的机会窗口,在这种情况下,您的TPL未观察到的异常仍将发生.


Yuv*_*kov 0

您可以通过自己实现中间件来处理请求超时。

IOwinContext有一个Request属性,它本身保存一个CancellationToken命名的属性CallCancelled,可以轮询该属性来查看请求是否被取消:

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.
             }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,存在一般异常子句,因为我不确定当 tge 请求被取消时,OWIN 是否会抛出 TCE,因此无论如何都要使用令牌来确定。

编辑:

正如 @usr 向我指出的那样,这个解决方案可以处理外部未被观察到的情况Task,这是我所做的一个相当大的假设。如果这是内部任务,我需要重新考虑解决方案。