I\'m interested in running a long process, I want to update the UI as soon as results start coming in and not wait for it to finish.
How would I go about this? I\'ve
You can run the long running task using ThreadPool.QueueUserWorkItem and it can update state in Application/Session which the user can poll. As you pointed that has some lifetime issues. Beyond what you pointed out, the app-pool could recycle - but maybe that's OK.
What is the logical scope of that operation? Is it a system type long running task that someone/many folks need to query the progress on? Or is it a long running task on behalf of a specific user? If it's the latter, then it's OK if that user's session times out, etc... If it's the former then you need to be more durable. for example, you could store the task request, the state and the progress in a database. That way on app restart it can just pickup where it eft off and its easy to query by anyone (a noter decision point if system level task).
The final consideration is whether you'll ever have more than one web roles (web farm/cluster). If that's ever a consideration than a DB or even separate worker role/service becomes more appropriate.
So it all boils down to the type of task it is, who needs to monitor it and what are the durability requirements. If its just a user task, keep it simple, queueuserworkitem and session state.
I was trying to solve something similar (reporting real-time progress from a long running server operation back to the client) recently and it turned out SignalR is a perfect fit for this situation.
Basically it is a library wrapping long-polling and Web Sockets, using (transparently) whatever is available on server and client.
I only have good experience with it so far.
This article seems to describe what you want, simple and w/o SignalR:
ASP.NET MVC 3: Async jQuery progress indicator for long running tasks
Controller:
public class HomeController : Controller { private static IDictionary tasks = new Dictionary();
public ActionResult Index()
{
return View();
}
public ActionResult Start()
{
var taskId = Guid.NewGuid();
tasks.Add(taskId, 0);
Task.Factory.StartNew(() =>
{
for (var i = 0; i <= 100; i++)
{
tasks[taskId] = i; // update task progress
Thread.Sleep(50); // simulate long running operation
}
tasks.Remove(taskId);
});
return Json(taskId);
}
public ActionResult Progress(Guid id)
{
return Json(tasks.Keys.Contains(id) ? tasks[id] : 100);
}
}
View:
<script type="text/javascript">
function updateMonitor(taskId, status) {
$("#" + taskId).html("Task [" + taskId + "]: " + status);
}
$(function () {
$("#start").click(function (e) {
e.preventDefault();
$.post("Home/Start", {}, function (taskId) {
// Init monitors
$("#monitors").append($("<p id='" + taskId + "'/>"));
updateMonitor(taskId, "Started");
// Periodically update monitors
var intervalId = setInterval(function () {
$.post("Home/Progress", { id: taskId }, function (progress) {
if (progress >= 100) {
updateMonitor(taskId, "Completed");
clearInterval(intervalId);
} else {
updateMonitor(taskId, progress + "%");
}
});
}, 100);
});
});
});