如何在长服务器进程中显示信息丰富的实时进度数据

我有这么长的过程可能需要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  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 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 ---> 1-2018以及整个过程在估计时间旁边的百分比。

要使用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中,您必须添加以下引用:

        

现在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('
  • ' + progress + ':  ' + functionName + '
  • '); }; // Start the connection. $.connection.hub.start().done(function() { $('#startlongprocess').click(function() { //start the long process hub.server.start("arg"); alert("started"); }); }); });

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

     

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

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

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

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

      或者在这里:

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

      举些例子。

      这是我认为可以解决您的问题的简化问题。 如果在任务中运行长操作,则可以使用状态对象更新应用程序。 如果您的应用程序是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(ref T field, T value, [CallerMemberName] string propertyName = null) { if (EqualityComparer.Default.Equals(field, value)) return false; OnPropertyChanging(propertyName); field = value; OnPropertyChanged(propertyName); return true; } }