我的流程很长,可能需要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 ---> 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-团体)
在给定的示例中,它仅向
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>: ' + 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 来自 signalr 团队)
您可以使用网络套接字将进度更新推送到客户端。 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<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;
}
}
可以帮助我使用 blazor(Web 应用服务器) ? 坦克