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

Jas*_*son 46 c# asp.net-mvc jquery design-patterns asp.net-mvc-2

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

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

感谢所有建议!

Dar*_*rov 51

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

控制器:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

索引()查看:

<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>
Run Code Online (Sandbox Code Playgroud)

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


ulu*_*ulu 6

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

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

GlobalHost.ConnectionManager.GetHubContext<LogHub>()
                              .Clients.Client(connectionId)
                              .log(message);
Run Code Online (Sandbox Code Playgroud)

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

$.connection.logHub.client.log = function(message){...};
Run Code Online (Sandbox Code Playgroud)

在我的博客文章的更多细节在这里

  • 当然,就上述问题,差不多2年前发布了有关SignalR的评论.你真的不应该只是发布一个链接到你的博客,因为这是边缘垃圾和链接的答案是不受欢迎的. (7认同)