1

This is the first time that I am using AsyncController (MVC3) and I am having trouble understanding the asynchronous action. My task is to import moderate amount of data from Excel to SQL Server database, using Entity Framework. I thought this would qualify for an async operation and so I wrote a controller explicitly for that purpose. Here is the controller code with async Action

public class CampDonorImportController : AsyncController
{
    public void CampDonorImportAsync(string fileName, int taskId) {
        ...
        HttpContext.Application["Progress" + taskId] = 0;
        AsyncManager.OutstandingOperations.Increment();
        Task.Factory.StartNew(task => {
            //Code to import Excel Data
        }, taskId);
    }

    public ActionResult CampDonorImportCompleted() {
        return null;
    }

    public ActionResult ReportImportProgress(int taskId) {
        ...
        return Json(new { Progress = progress, CarryOn = carryOn, Status = status }, JsonRequestBehavior.AllowGet);

    }

I call the Async action (CampDonorImportAsync) using following JQuery code

    $.ajax({
        url: importDonorUrl,
        success: function (data, textStatus, jqXHR) {
            //Repeatedly call reportImportProgress
            refreshTimerId = window.setInterval(reportImportProgress, reportTaskProgressTime);
        },
    });

The reportImportProgress javascript function calls the ReportImportProgress() action which displays the current progress of the async action.

    $.ajax({
        url: reportTaskProgressUrl,
        complete: function (jqXHR, textStatus, errorThrown) {
            var json = $.parseJSON(jqXHR.responseText);
            if (!json.CarryOn) {
                endImportInit();
                alert(json.Status);
            }
        },
    });

The issue is that the call to the Async method(CampDonorImportAsync) blocks other requests from the same page, for example ReportImportProgress() above. I thought that the call to Async action should return immediately, so that other requests can be made, even if the Async task is still going on. I am not sure why the Async request gets blocked and waits for the task to complete, instead of returning immediately. Any Ideas ?

Jatin
  • 4,023
  • 10
  • 60
  • 107

1 Answers1

3

As I describe on my blog, async does not change the HTTP protocol. With HTTP, you get one response per request, and that's it.

A reliable solution is somewhat more complex than what you're thinking. You need a reliable queue (e.g., Azure queue) and an independent backend (e.g., Azure worker role) that processes requests from that queue. Then, your MVC controller can just add a request to the queue and return. Your frontend can then poll for completion or be notified via something like SignalR.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • Yes, I kind of misunderstood the whole Async action. Its only the IIS thread that is returned to the pool, the Request still lives. Actually, I stumbled across a possible solution that works. Here is the link http://blog.janjonas.net/2012-01-02/asp_net-mvc_3-async-jquery-progress-indicator-long-running-tasks. Actually, the Action method which starts the new task returns (without waiting for the background task), and then I can call my ReprotImportProgress action at regular intervals. It works, but I am not sure of downsides (if any) – Jatin Dec 05 '13 at 14:34
  • 1
    @Nirvan: Keeping long-running tasks in memory like the link you posted is quite dangerous. I have [another blog post](http://blog.stephencleary.com/2012/12/returning-early-from-aspnet-requests.html) that looks at this in more detail. – Stephen Cleary Dec 05 '13 at 14:58
  • I have implemented the solution that is mentioned in your blog post. I have tested it and the Cancellation Token feature allows the task to gracefully exit. Generally, we will be backing up our database before the background processing and so your solution suits well for my case. – Jatin Dec 09 '13 at 09:42