在 ASP.NET MVC 中通过 CancellationToken 中止 AJAX 请求

fro*_*ova 7 c# asp.net-mvc asp.net-ajax task-parallel-library cancellation

我有一个与外部资源交互的 ASP.NET MVC 应用程序,并且有一个操作需要很多时间。所以在控制器中我有这样的方法

[HttpPost]
public async Task<JsonResult> SomeMethod(..., CancellationToken token)
{
    await _someService.ExecuteSlowOperationAsync(..., token);
    ...
}
Run Code Online (Sandbox Code Playgroud)

这个缓慢的操作看起来像

public async Task ExecuteSlowOperationAsync(..., CancellationToken token)
{
    return await Task.Run(() => 
    {
       //interacting with external resource
    }, token);
} 
Run Code Online (Sandbox Code Playgroud)

此方法与模态视图相关联,如果请求将花费太多时间,用户可能会决定关闭它。据此,我必须在不等待结果的情况下取消请求,因此在客户端我有类似的代码

...
var request = $.ajax(...);

...

$('#modal').on('hidden.bs.modal', function () {
  request.abort();
});
Run Code Online (Sandbox Code Playgroud)

如果我正确理解这篇文章,取消令牌通过框架模型绑定器与请求绑定,无需对其进行任何操作。当用户关闭模态表单时,在浏览器控制台中,我可以看到请求状态“已取消”,但在服务器端缓慢的操作仍在执行。Aslo我试过

CancellationToken disconnectedToken = Response.ClientDisconnectedToken;            
var source = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, disconnectedToken);
Run Code Online (Sandbox Code Playgroud)

然后从这个来源拿了令牌,但还是一无所获。

我觉得我错过了一些重要的事情并且对这种情况有误解。任何想法如何使它工作?

小智 3

我的回答可能有点晚了。但我想我发现了你的问题。使用Task.Run(Action action, CancellationToken cancellationToken)取消长时间运行的操作可能并不总是有效。

在 Task.Run 中使用 CancellationToken 超时不起作用

如果长时间运行的操作是在您自己的代码中,您应该编写如下内容

public async Task ExecuteSlowOperationAsync(..., CancellationToken token)
{
    while (true)
    {
        if(cancelToken.IsCancellationRequested)
        {
            return;
        }
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您无法修改长时间运行的操作本身的源代码,您可以使用WithCancellationMicrosoft.VisualStudio.Threading.ThreadingTools (有关更多信息,请参阅/sf/answers/2337765251/ ),您甚至可以更早地使用它在您的控制器上

[HttpPost]
public async Task<JsonResult> SomeMethod(..., CancellationToken token)
{
     CancellationToken disconnectedToken = Response.ClientDisconnectedToken;            
     var linkedToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, disconnectedToken).Token;
     await _someService.ExecuteSlowOperationAsync(...).WithCancellation(linkedToken);
     ...
}
Run Code Online (Sandbox Code Playgroud)