How to show an informative real time progress data

2019-03-18 14:30发布

问题:

I have a so long process may take 1 hour .

This process consists of many steps run from year to year .My main problem is :

How to provide an informative real time progress to the end user during the process not just a dummy loading bar.

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


                }
            }

I want to show the current step for example : (Promotion 1 ) in ---> 1-2018 and the percentage of the whole process beside the estimated time .

回答1:

To report progress of a very long task using signalR you can do something like this (it's only an example to show how it can work):

Server part

We start by mapping SignalR.

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

We create a hub class (don't forget to install the signalr package):

(If you want to report progress to all connected users or a specific group of users take a look here : http://www.asp.net/signalr/overview/guide-to-the-api/working-with-groups)

In the given example it report progress only to the caller of the Start function.

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

Client Part

In the html you must add these references :

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

and now the Js part to get the progress from the hub :

 $(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");
     });
   });
 });

The html container for the progress and the start button :

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

If you need more explanations don't hesitate to ask.

( My example is based on this one http://www.asp.net/signalr/overview/getting-started/tutorial-getting-started-with-signalr from signalr team )



回答2:

You can use web sockets to push progress updates down to the client. SignalR is a dotnet library that wraps websockets and falls back where websockets isn't available. There are comprehensive examples online showing how to implement progress reporting using SignalR so no need to repeat them. Have a look here:

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

or here:

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

for examples.



回答3:

Here is a simplification of your problem that I think will solve your problem. If you run your long operation in a task, you can update the application with a status object. If your application is WPF, and you bind the status, it will update automatically. In WinForms, you can bind or just implement the event handler.

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


}