views:

223

answers:

1

I've been trying to create a controller in my project for delivering what could turn out to be quite complex reports. As a result they can take a relatively long time and a progress bar would certainly help users to know that things are progressing. The report will be kicked off via an AJAX request, with the idea being that periodic JSON requests will get the status and update the progress bar.

I've been experimenting with the AsyncController as that seems to be a nice way of running long processes without tying up resources, but it doesn't appear to give me any way of checking on the progress (and seems to block further JSON requests and I haven't discovered why yet). After that I've tried resorting to storing progress in a static variable on the controller and reading the status from that - but to be honest that all seems a bit hacky!

All suggestions gratefully accepted!

+5  A: 

Here's a sample I wrote that you could try:

Controller:

public class HomeController : AsyncController
{
    public ActionResult Index()
    {
        return View();
    }

    public void SomeTaskAsync(int id)
    {
        AsyncManager.OutstandingOperations.Increment();
        Task.Factory.StartNew(taskId =>
        {
            for (int i = 0; i < 100; i++)
            {
                Thread.Sleep(200);
                HttpContext.Application["task" + taskId] = i;
            }
            var result = "result";
            AsyncManager.OutstandingOperations.Decrement();
            AsyncManager.Parameters["result"] = result;
            return result;
        }, id);
    }

    public ActionResult SomeTaskCompleted(string result)
    {
        return Content(result, "text/plain");
    }

    public ActionResult SomeTaskProgress(int id)
    {
        return Json(new
        {
            Progress = HttpContext.Application["task" + id]
        }, JsonRequestBehavior.AllowGet);
    }
}

View:

<script type="text/javascript">
$(function () {
    var taskId = 543;
    $.get('/home/sometask', { id: taskId }, function (result) {
        window.clearInterval(intervalId);
        $('#result').html(result);
    });

    var intervalId = window.setInterval(function () {
        $.getJSON('/home/sometaskprogress', { id: taskId }, function (json) {
            $('#progress').html(json.Progress + '%');
        });
    }, 5000);
});
</script>

<div id="progress"></div>
<div id="result"></div>

The idea is to start an asynchronous operation that will report the progress using HttpContext.Application meaning that each task must have an unique id. Then on the client side we start the task and then send multiple AJAX requests (every 5s) to update the progress. You may tweak the parameters to adjust to your scenario. Further improvement would be to add exception handling.

Darin Dimitrov
Thanks Darin, I tried your code but still get all of the requests 'backing up' after one another. Having spent this morning slowly trawling for an answer I'm now starting to think that this could be related to Session not allowing multiple requests simultaneously, forcing requests to be synchronous.One change I had to make was your use of Task.Factory since I'm currently working in .NET 3, not 4. Do you think Task.Factory would resolve the Session locking problem?
Jason
Additionally I have just got your code running and I did it by removing any code in my controller that refers to Session, so that's obviously my problem. Unfortunately my controllers all require various bits of data I'm holding in Session (and retrieved within a BaseController that they all extend) so I can't see any simple way around that problem.
Jason
The Session is not always available in asynchronous actions.
Darin Dimitrov