操作完成前MVC4 + async / await +返回响应

时间:2012-09-07 14:01:07

标签: c# asynchronous asp.net-mvc-4 .net-4.5 async-await

在我的MVC4应用程序中,我需要添加一个控制器来上传和处理大文件。文件上传后,我需要立即启动该文件的异步处理并将响应返回给浏览器,而不必等待处理完成。

显然我可以手动启动一个新线程来处理文件,但我想知道我是否可以使用.net 4.5引入的async / await机制实现这个场景

为了测试这个概念,我尝试过这样的事情:

public async Task<ActionResult> Test()
{
    TestAsync();
    return View("Test");
}

public async void TestAsync()
{
    await LongRunning();
}

private Task<int> LongRunning()
{
    return Task<int>.Factory.StartNew(() => Pause());
}

private int Pause()
{
    Thread.Sleep(10000);
    return 3;
}

异步机制似乎一般都有效:当我调试代码时,我点击了“返回视图(”测试“);”行“返回3”之前的行。但是,浏览器仅在Pause方法完成后才会收到响应。

这似乎表现得像常规异步控制器(具有Async和Completed方法的控制器)。有没有办法在控制器中为我的场景使用async / await?

3 个答案:

答案 0 :(得分:14)

  

显然我可以手动启动一个新线程来处理文件,但我想知道我是否可以使用.net 4.5引入的async / await机制实现这个场景

不,你不能,因为async doesn't change the HTTP protocol

Svick和James已经发布了正确的答案作为评论,为方便起见我在下面重复:

  

IIS可以随时回收您的应用程序。

     

如果您有长时间运行的事情要求异步,请在其他地方执行。 “典型”是一个持久性队列(MSMQ,Azure,RabbitMQ等),其他东西(Windows服务,由任务调度程序运行的exe,使用Quartz.net的应用程序等)处理它们。

总而言之,HTTP为您提供了一个请求和一个响应(async - 以及其他任何内容 - 不会更改此内容。)

ASP.NET是围绕HTTP请求设计的(并假设“如果没有未完成的请求,则可以安全地停止此网站”)。你可以启动一个新线程,并将你的上传保存在内存中(这是最简单的方法),但强烈建议不要这样做。

根据您的情况,我建议您遵循James的建议:

  • 上传完成后,将上传内容保存到永久存储(例如Azure队列),然后将“票证”返回给浏览器。
  • 让其他进程(例如,Azure辅助角色)处理队列,最终将其标记为完成。
  • 让浏览器使用“票证”轮询服务器。服务器使用“票证”在持久存储中查找文件,并返回它是否完整。

这有一些变化(例如,使用SignalR在处理完成时通知浏览器),但一般架构是相同的。

这很复杂,但这是正确的方法。

答案 1 :(得分:1)

您的代码中有错误。 您必须等待操作中对TestAsync的调用。 如果不这样做,它只返回一个“任务”对象而不运行任何东西。

public async Task<ActionResult> Test()
{
    await TestAsync();
    return View("Test");
}

并且在异步方法中睡眠的正确方法是调用

await Task.Delay(10000);

您必须更改TestAsync的签名:如果方法返回void,则不应将方法标记为异步。它必须返回Task。保留返回void以与.net事件兼容。

public async Task TestAsync()
{
    await LongRunning();
}

方法体是一样的。

答案 2 :(得分:0)

你的LongRunning方法同步睡眠10秒。改变它,以便在任务中发生睡眠。