Web API只允许单个异步任务。

3

如何处理单个异步操作的适当场景?例如,我需要导入大文件,并在导入过程中禁用该选项,以确保不会触发第二个导入。

首先想到的是:

[HttpPost]
public async Task<HttpResponseMessage> ImportConfigurationData()
{
    if (HttpContext.Current.Application["ImportConfigurationDataInProcess"] as bool? ?? false)
            return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "Task still running");

    HttpContext.Current.Application["ImportConfigurationDataInProcess"] = true;

    string root = HttpContext.Current.Server.MapPath("~/App_Data");
    var provider = new MultipartFormDataStreamProvider(root);
    await Request.Content.ReadAsMultipartAsync(provider);

    //actual import

    HttpContext.Current.Application["ImportConfigurationDataInProcess"] = false;
    Request.CreateResponse(HttpStatusCode.OK, true)
}

但这似乎是一种非常硬编码的解决方案。如何正确处理呢? 另一件事是,它在客户端上工作不够正常,因为它仍在等待响应。所以用户是否可以只将该文件发送到服务器而不等待其完成,而是在文件发送到服务器后重新加载页面,而不必等待 await 的内容完成。

1个回答

6
< p > async 不会改变 HTTP 协议 (如我在博客中所解释的)。 因此,您仍然只能获得每个请求的一个响应。

正确的解决方案是将工作的“令牌”(和导入数据)保存到可靠存储(例如 Azure 表/队列)中,并具有单独的处理后端来执行实际导入。

ImportConfigurationData 操作将检查该数据是否已存在令牌,并在发现时拒绝请求。


谢谢您的回答。但我不打算更改协议。我只想以某种方式启动任务,并在任务仍在处理过程中调用该请求时发送一些错误响应。 - Vladimirs
我理解并能接受这种解决方案所带来的风险。但我无法仅为此目的设置Azure。我尝试了Task.Factory.StartNew(async () => //Some import code); 但是在await Request.Content.ReadAsMultipartAsync(provider);之后,它不喜欢async(),似乎我的任务附加到了主线程上。 - Vladimirs
1
@Vladimirs:我的帖子的重点是async不会改变HTTP协议。如果你正在使用HTTP,那么每个请求只能得到一个响应。因此,如果你想在导入完成之前向客户端返回响应,那么你需要一个更复杂的解决方案。Azure只是可靠存储的一个例子;你不必使用Azure来解决这个问题。 - Stephen Cleary
所以基本上您建议将任务状态存储在数据库中,而不是HttpContext.Current.Application中。或者您的意思是在Azure上运行任务? - Vladimirs
1
@Vladimirs: 是的,数据库是可靠的存储方式,所以那样做没问题。 - Stephen Cleary

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接