In my MVC4 app I need to add a controller for uploading and processing large files. Immediately after the file is uploaded I need to start async processing of that file and return response to the browser without waiting for the processing to complete.
Obviously I could start a new thread for processing the file manually, but I'm wondering if I can implement this scenario using async/await mechanism introduced with .net 4.5
To test the concept I've tried something like this:
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;
}
The async mechanism seems to work in general: when I debug the code I hit the "return View("Test");" line before the line "return 3". However, the browser receives the response only after the Pause method completes.
This seems to behave like regular async controllers (the ones with Async and Completed methods). Is there a way to use async/await in controllers for my scenario?
Your LongRunning method is synchronously sleeping 10 seconds. Change it so the sleep happens in the task instead.
No, you cannot, because
async
doesn't change the HTTP protocol.Svick and James have already posted the correct answers as comments, which I duplicate below for convenience:
To summarize, HTTP gives you one request and one response (
async
- and anything else - won't change this).ASP.NET is designed around HTTP requests (and makes assumptions like "if there are no outstanding requests, then it's safe to stop this web site"). You can kick off a new thread and keep your upload in memory (which is the easiest way to do it), but it's strongly not recommended.
For your situation, I recommend you follow James' suggestion:
There are some variations to this (e.g., using SignalR to notify the browser when processing is complete), but the general architecture is the same.
This is complex, but it's the right way to do it.
You have an error in your code. You must await the call to TestAsync in your action. If you don't, it simply returns a "Task" object without running anything.
and the correct way to sleep in an async method is to call
You have to change the signature of TestAsync: you should not mark a method async if it returns void. It must return Task instead. Returning void is reserved for compatibility with .net events.
The method body is the same.