代码之家  ›  专栏  ›  技术社区  ›  Jason

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

  •  43
  • Jason  · 技术社区  · 15 年前

    我一直在尝试在我的项目中创建一个控制器,用于交付可能非常复杂的报告。因此,它们可能需要相当长的时间,而进度条肯定会帮助用户了解事情的进展情况。报告将通过Ajax请求启动,其思想是定期的JSON请求将获得状态并更新进度条。

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

    感谢您接受所有建议!

    3 回复  |  直到 6 年前
        1
  •  45
  •   David Silva-Barrera    6 年前

    以下是我写的一个示例,您可以尝试:

    控制器:

    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个)来更新进度。您可以调整参数以适应您的场景。进一步的改进将是添加异常处理。

        2
  •  6
  •   ulu    10 年前

    4.5年后,这个问题得到了回答,我们有一个图书馆,可以使这个任务更容易:信号。不需要使用共享状态(这很糟糕,因为它会导致意外的结果),只需使用HubContext类连接到将消息发送到客户端的集线器。

    首先,我们像往常一样设置信号器连接(参见 here ,除了我们的中心不需要任何服务器端方法。然后,我们对端点/控制器/任何对象进行Ajax调用,并传递连接ID,我们通常会得到: var connectionId = $.connection.hub.id; . 在服务器端,您可以在不同的线程上启动进程,并将200重新发送给客户机。进程将知道connectionID,以便将消息发送回客户机,如下所示:

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

    在这里, log 是要从服务器调用的客户端方法,因此应该像通常使用信号器那样定义它:

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

    我的博客文章中有更多详细信息 here

        3
  •  0
  •   Dovlet Mamenov Mina Gabriel    10 年前

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

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