需要一个带有用户反馈的ASP.NET MVC长时间运行过程

时间:2010-05-28 07:24:09

标签: c# jquery asp.net-mvc design-patterns asp.net-mvc-2

我一直在尝试在我的项目中创建一个控制器来提供可能会变得非常复杂的报告。因此,他们可能需要相对较长的时间,进度条肯定会帮助用户知道事情正在发展。该报告将通过AJAX请求启动,其想法是定期JSON请求将获取状态并更新进度条。

我一直在尝试使用AsyncController,因为这似乎是在不占用资源的情况下运行长进程的好方法,但它似乎没有给我任何检查进度的方法(并且似乎进一步阻止JSON请求,我还没有发现原因)。在那之后,我尝试将控制器中的静态变量存储进度并从中读取状态 - 但说实话,所有看起来都有点hacky!

感谢所有建议!

3 个答案:

答案 0 :(得分:46)

以下是我写的一个你可以试试的样本:

控制器:

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);
    }
}

索引()查看:

<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>

我们的想法是启动一个异步操作,它将使用HttpContext.Application报告进度,这意味着每个任务必须具有唯一的ID。然后在客户端,我们启动任务,然后发送多个AJAX请求(每5秒)来更新进度。您可以调整参数以根据您的方案进行调整。进一步改进将是添加异常处理。

答案 1 :(得分:6)

这个问题得到解答后的4。5年,我们有一个可以使这项任务变得更容易的库:SignalR。不需要使用共享状态(这很糟糕,因为它可能导致意外结果),只需使用HubContext类连接到向客户端发送消息的集线器。

首先,我们像往常一样设置SignalR连接(例如here),但我们的Hub上不需要任何服务器端方法。然后我们对我们的Endpoint / Controller /中的任何一个进行AJAX调用并传递连接ID,我们像往常一样:var connectionId = $.connection.hub.id;。在服务器端,您可以在不同的线程上启动您的进程,并向客户端重新启动200 OK。该进程将知道connectionId,以便它可以将消息发送回客户端,如下所示:

GlobalHost.ConnectionManager.GetHubContext<LogHub>()
                              .Clients.Client(connectionId)
                              .log(message);

这里,log是您想要从服务器调用的客户端方法,因此它应该像您通常使用SignalR一样定义:

$.connection.logHub.client.log = function(message){...};

我的博文here

中的更多详细信息

答案 2 :(得分:0)

如果您可以选择访问Web服务器计算机,则可以创建Windows服务或简单的控制台应用程序来执行长时间运行的工作。您的Web应用程序应该向db添加记录以指示应该启动操作,并且定期检查db中的新记录的Windows服务应该开始执行任务并向db报告进度。 您的网络应用可以使用ajax请求来检查进度并向用户显示。

我使用此方法为asp.net mvc应用程序实现excel报告。 此报告由Windows服务创建,该服务在计算机上运行并不断检查报告表中的新记录,当它找到新记录时,它开始创建报告并通过更新记录字段指示进度。 Asp.net mvc应用程序只是在数据库中添加了新的报告记录和跟踪进度,直到它完成,然后提供了一个准备文件的下载链接。