【问题标题】:How to show an informative real time progress data during long server process如何在长服务器进程中显示信息丰富的实时进度数据
【发布时间】:2016-09-29 05:08:06
【问题描述】:

我有一个很长的过程可能需要 1 小时。

这个过程包括每年运行的许多步骤。我的主要问题是:

如何在过程中向最终用户提供信息丰富的实时进度,而不仅仅是一个虚拟加载栏。

            int index = Convert.ToInt32(e.CommandArgument);
            bool done = false;
            int res = -1;
            int fromVal = int.Parse(gv_balance.Rows[index].Cells[0].Text);
            int toVal = int.Parse(gv_balance.Rows[index].Cells[1].Text);
            int finMonth = 1;
            int finYear = 0;
            int EndServ = 0;
            int calcYear = int.Parse(gv_balance.Rows[index].Cells[2].Text);
            int total;
            total = ((toVal - fromVal) + 1);
            string msg = string.Empty;

            int confirm = Balance.GetConfirmState(calcYear);
            if (confirm == 0)
            {
                RadProgressContext progress = RadProgressContext.Current;
                progress.Speed = "N/A";

                finYear = fromVal;

                for (int i = fromVal; i <= toVal; i++)
                {
                    decimal ratio;
                    //Load New Employees
                    if (toVal - fromVal > 0)
                    {
                        ratio = ((decimal)toVal - i) / (toVal - fromVal) * 100;
                    }
                    else
                    {
                        ratio = ((decimal)toVal - i) / 1 * 100;
                    }
                    progress.PrimaryTotal = total;
                    progress.PrimaryValue = total;
                    progress.PrimaryPercent = 100;

                    progress.SecondaryTotal = 100; // total;
                    progress.SecondaryValue = ratio;//i ;
                    progress.SecondaryPercent = ratio; //i;


                    progress.CurrentOperationText = "Step " + i.ToString();
                    if (!Response.IsClientConnected)
                    {
                        //Cancel button was clicked or the browser was closed, so stop processing
                        break;
                    }

                    progress.TimeEstimated = (toVal - i) * 100;
                    //Stall the current thread for 0.1 seconds
                    System.Threading.Thread.Sleep(100);
                    EndServ = i + 1;
                    if (i == fromVal)
                    {   
                        //--->STEP1
                        //Load intial data 
                        int intial = Balance.PrepareIntialData(calcYear);
                        //--->STEP2
                        res = Balance.CalcEndServed(calcYear, EndServ - 1, 6, 30);

                    }
                     //--->STEP3
                    int newEmps = Balance.PrepareNewEmployees(calcYear, i);

                    for (int j = 0; j < 2; j++)
                    {
                        if (j == 0)
                        {
                            finMonth = 7;
                            finYear = i;

                        }
                        else
                        {
                            finMonth = 1;
                            finYear = i + 1;
                        }
                        //--->STEP4
                        int promotion1 = Balance.PreparePromotionFirst(finYear, finMonth, calcYear);
                         //--->STEP5
                        int promotion2 = Balance.PreparePromotionSecond(finYear, finMonth, calcYear);
                         //--->STEP6
                        int appointment1 = Balance.PrepareAppointmentFirst(finYear, finMonth, calcYear);
                         //--->STEP7
                        int appointment2 = Balance.PrepareAppointmentSecond(finYear, finMonth, calcYear);

                        //--->STEP8
                        int bonus = Balance.PrepareBonus(finMonth, finYear, 0, calcYear);

                         //--->STEP9
                        int salary = Balance.PrepareSalary(finYear, finMonth, calcYear);
                     (((CheckBox)gv_balance.Rows[index].Cells[3].FindControl("chk_redirect")).Checked == true)
                        {
                           //--->STEP9
                            int acco = Balance.PrepareFinanceAccount(finYear, finMonth, calcYear);
                        }

                    }


                    //--->STEP10
                    res = Balance.CalcEndServed(calcYear, EndServ, 6, 30);
                    Balance.CalcStudy(calcYear);
                    UpdateProgressContext();
                    if (res < 0)
                    {

                        success_lb.Visible = false;
                        error_lb.Visible = true;
                        error_lb.Text = "ERROR";

                    }
                    else
                    {
                        done = true;
                        success_lb.Visible = true;
                        error_lb.Visible = false;
                        success_lb.Text = "Success";
                    }


                }
            }

我想显示当前步骤,例如: (Promotion 1 ) in ---&gt; 1-2018 以及整个过程中估计时间之外的百分比。

【问题讨论】:

  • 查看服务器广播教程first
  • 那么你尝试了什么,什么没用?不清楚是什么问题。
  • @Evk:我确实尝试了上面的代码。加载器没有实时数据,只是虚拟加载器。我想要一个实时进度条,根据年份阐明我的步骤
  • 我建议您使用网络套接字实时通知/通知用户请求的进度。初始请求将启动该过程并订阅通知。然后,您可以使用 SignalR 保持客户端和服务器之间的连接,并将任何进度信息推送到客户端。
  • @Nkosi 如果您能编写代码来阐明这个想法,我将不胜感激

标签: c# asp.net ajax signalr progress-bar


【解决方案1】:

要使用 signalR 报告一个非常长的任务的进度,您可以执行以下操作(这只是一个示例,说明它是如何工作的):

服务器部分

我们首先映射 SignalR。

public class Startup
{
   public void Configuration(IAppBuilder app)
   {
       // Any connection or hub wire up and configuration should go here
       app.MapSignalR();
   }
}

我们创建一个 hub 类(别忘了安装 signalr 包):

(如果您想向所有连接用户或特定用户组报告进度,请查看此处:http://www.asp.net/signalr/overview/guide-to-the-api/working-with-groups

在给定的示例中,它仅向Start 函数的调用者报告进度。

public class MyHub : Hub
{     
    public void Start(string arg)
    {
        Task.Run(() =>
        {
            AVeryLongTask();
        });
    }

    //simulate a long task
    void AVeryLongTask()
    {
        for (int i = 0; i < 10000; i++)
        {
            Thread.Sleep(100);              
            Clients.Caller.ReportProgress("AVeryLongTask", i * 100 / 10000);
        }
    }
}

客户端部分

您必须在 html 中添加这些引用:

<!--Script references. -->
<!--Reference the jQuery library. -->
<script src="Scripts/jquery-1.6.4.min.js"></script>
<!--Reference the SignalR library. -->
<script src="/Scripts/jquery.signalR-2.0.0.js"></script>
<!--Reference the autogenerated SignalR hub script. -->
<script src="/signalr/hubs"></script>

现在是从集线器获取进度的 Js 部分:

 $(function() {
   // Declare a proxy to reference the hub.
   var hub = $.connection.myHub;
   // Create a function that the hub can call to report progress.
   hub.client.reportProgress = function(functionName, progress) {
     $('#progression').append('<li><strong>' + progress + '</strong>:&nbsp;&nbsp;' + functionName + '</li>');
   };
   // Start the connection.
   $.connection.hub.start().done(function() {
     $('#startlongprocess').click(function() {
      //start the long process
       hub.server.start("arg");
       alert("started");
     });
   });
 });

进度和开始按钮的 html 容器:

<div class="container">    
   <input type="button" id="startlongprocess" value="Send" />
   <ul id="progression"></ul>
</div>

如果您需要更多解释,请不要犹豫。

(我的示例基于来自信号员团队的http://www.asp.net/signalr/overview/getting-started/tutorial-getting-started-with-signalr

【讨论】:

    【解决方案2】:

    您可以使用网络套接字将进度更新推送到客户端。 SignalR 是一个 dotnet 库,它封装了 websockets 并在 websockets 不可用的地方回退。网上有全面的示例展示了如何使用 SignalR 实现进度报告,因此无需重复。看看这里:

    https://github.com/dragouf/SignalR.Progress

    或这里:

    https://www.safaribooksonline.com/blog/2014/02/06/server-side-signalr/

    例如。

    【讨论】:

      【解决方案3】:

      这是对您的问题的简化,我认为可以解决您的问题。如果您在任务中运行长操作,则可以使用状态对象更新应用程序。如果您的应用程序是 WPF,并且您绑定了状态,它将自动更新。在 WinForms 中,您可以绑定或仅实现事件处理程序。

          void Main()
      {
          var status = new Status();
          object locker = new object();
      
          status.PropertyChanged += Status_PropertyChanged;
      
          //
          // Long running job in a task
          //
          var task = new Task((s) => 
              {
      
                  for(int i = 0; i < 1000; i++)
                  {
      
                      Task.Delay(TimeSpan.FromSeconds(5)).Wait();
                      //Thread.Sleep(5000);
                      lock (locker)
                      {
                          status.Message = string.Format("Iteration: {0}", i);
                      }
      
                  }
      
      
              }, status);
      
          //
          // start and wait for the task to complete.  In a real application, you may end differently
          task.Start();
          task.Wait();
      
      }
      
      
      static void Status_PropertyChanged(object sender,
          PropertyChangedEventArgs e)
      {
      
      
          var s = sender as Status;
      
          if(s != null && string.Equals(e.PropertyName, "Message"))
          {
              Console.WriteLine( s.Message);
          }
      }
      
      
      public class Status : PropertyNotifier
      {
      
          private string _Message = string.Empty;
      
          public string Message
          {
              get { return _Message; }
              set { SetField(ref _Message, value); }
          }
      
      }
      
      public abstract class PropertyNotifier : INotifyPropertyChanged, INotifyPropertyChanging
      {
      public event PropertyChangingEventHandler PropertyChanging;
      public event PropertyChangedEventHandler PropertyChanged; // ? = new Delegate{};
      
          protected virtual void OnPropertyChanged(string propertyName)
          {
      
              PropertyChangedEventHandler handler = PropertyChanged;
      
              if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
          }
      
          protected virtual void OnPropertyChanging(string propertyName)
          {
              PropertyChangingEventHandler handler = PropertyChanging;
      
              if (handler != null) handler(this, new PropertyChangingEventArgs(propertyName));
          }
      
          protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
          {
              if (EqualityComparer<T>.Default.Equals(field, value)) return false;
              OnPropertyChanging(propertyName);
              field = value;
              OnPropertyChanged(propertyName);
              return true;
          }
      
      
      }
      

      【讨论】:

      • 非常感谢,但它是 asp.net 网络表单应用程序而不是 WPF
      • 那么信号 R 将是您的最佳选择。我建议在堆栈中搜索它
      • @Jim 不要将 Thread.Sleep 与 Task 框架混合使用。 Thread.Sleep 将导致底层线程阻塞。它们在语义上不安全。改用 Task.Delay
      • 约翰。感谢您的提示并纠正我的示例。在对线程进行了更多阅读/工作后,我一直在使用 Task.Delay。
      猜你喜欢
      • 2013-02-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-01
      • 1970-01-01
      • 2010-09-09
      • 2016-01-14
      相关资源
      最近更新 更多