使用ExecuteNonQueryAsync和Reporting Progress

我以为我试图做一些非常简单的事情。 我只想在屏幕上报告一个正在运行的号码,以便用户知道我正在执行的SQL存储过程正在运行,并且他们不会感到不耐烦并开始点击按钮。

问题是我无法弄清楚如何实际调用ExecutNonQueryAsync命令的进度报告器。 它停留在我的报告循环中并且从不执​​行命令但是,如果我把它放在async命令之后,它将被执行并且结果永远不会等于零。

任何想法,评论,想法将不胜感激。 非常感谢!

int i = 0; lblProcessing.Text = "Transactions " + i.ToString(); int result = 0; while (result==0) { i++; if (i % 500 == 0) { lblProcessing.Text = "Transactions " + i.ToString(); lblProcessing.Refresh(); } } // Yes - I know - the code never gets here - that is the problem! result = await cmd.ExecuteNonQueryAsync(); 

您是否只想让用户知道发生了什么事情,而您实际上并不需要显示当前进度?

如果是这样,您可以显示ProgressBar ,其Style设置为Marquee

如果您希望这是一种“自包含”方法,则可以在模态表单上显示进度条,并在表单本身中包含表单代码。

例如

 public void ExecuteNonQueryWithProgress(SqlCommand cmd) { Form f = new Form() { Text = "Please wait...", Size = new Size(400, 100), StartPosition = FormStartPosition.CenterScreen, FormBorderStyle = FormBorderStyle.FixedDialog, MaximizeBox = false, ControlBox = false }; f.Controls.Add(new ProgressBar() { Style = ProgressBarStyle.Marquee, Dock = DockStyle.Fill }); f.Shown += async (sender, e) => { await cmd.ExecuteNonQueryAsync(); f.Close(); }; f.ShowDialog(); } 

您无法在此处执行ExecuteNonQueryAsync来执行您想要的操作。 要执行您要查找的内容,该方法的结果必须是逐行或在SQL调用期间递增的块中,但这不是如何将查询批处理提交到SQL Server的工作方式或实际上您希望如何从头顶上看工作。 将SQL语句传递给服务器,在完成语句处理后,它将返回受语句影响的总行数。

最简单的方法是使用第二个连接来监视进度并报告它。 这里有一个小样本可以帮助您入门:

 using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Text; using System.Threading.Tasks; namespace Microsoft.Samples.SqlServer { public class SessionStats { public long Reads { get; set; } public long Writes { get; set; } public long CpuTime { get; set; } public long RowCount { get; set; } public long WaitTime { get; set; } public string LastWaitType { get; set; } public string Status { get; set; } public override string ToString() { return $"Reads {Reads}, Writes {Writes}, CPU {CpuTime}, RowCount {RowCount}, WaitTime {WaitTime}, LastWaitType {LastWaitType}, Status {Status}"; } } public class SqlCommandWithProgress { public static async Task ExecuteNonQuery(string ConnectionString, string Query, Action OnProgress) { using (var rdr = await ExecuteReader(ConnectionString, Query, OnProgress)) { rdr.Dispose(); } } public static async Task ExecuteDataTable(string ConnectionString, string Query, Action OnProgress) { using (var rdr = await ExecuteReader(ConnectionString, Query, OnProgress)) { var dt = new DataTable(); dt.Load(rdr); return dt; } } public static async Task ExecuteReader(string ConnectionString, string Query, Action OnProgress) { var mainCon = new SqlConnection(ConnectionString); using (var monitorCon = new SqlConnection(ConnectionString)) { mainCon.Open(); monitorCon.Open(); var cmd = new SqlCommand("select @@spid session_id", mainCon); var spid = Convert.ToInt32(cmd.ExecuteScalar()); cmd = new SqlCommand(Query, mainCon); var monitorQuery = @" select s.reads, s.writes, r.cpu_time, s.row_count, r.wait_time, r.last_wait_type, r.status from sys.dm_exec_requests r join sys.dm_exec_sessions s on r.session_id = s.session_id where r.session_id = @session_id"; var monitorCmd = new SqlCommand(monitorQuery, monitorCon); monitorCmd.Parameters.Add(new SqlParameter("@session_id", spid)); var queryTask = cmd.ExecuteReaderAsync( CommandBehavior.CloseConnection ); var cols = new { reads = 0, writes = 1, cpu_time =2,row_count = 3, wait_time = 4, last_wait_type = 5, status = 6 }; while (!queryTask.IsCompleted) { var firstTask = await Task.WhenAny(queryTask, Task.Delay(1000)); if (firstTask == queryTask) { break; } using (var rdr = await monitorCmd.ExecuteReaderAsync()) { await rdr.ReadAsync(); var result = new SessionStats() { Reads = Convert.ToInt64(rdr[cols.reads]), Writes = Convert.ToInt64(rdr[cols.writes]), RowCount = Convert.ToInt64(rdr[cols.row_count]), CpuTime = Convert.ToInt64(rdr[cols.cpu_time]), WaitTime = Convert.ToInt64(rdr[cols.wait_time]), LastWaitType = Convert.ToString(rdr[cols.last_wait_type]), Status = Convert.ToString(rdr[cols.status]), }; OnProgress(result); } } return queryTask.Result; } } } } 

您会称之为:

  class Program { static void Main(string[] args) { Run().Wait(); } static async Task Run() { var constr = "server=localhost;database=tempdb;integrated security=true"; var sql = @" set nocount on; select newid() d into #foo from sys.objects, sys.objects o2, sys.columns order by newid(); select count(*) from #foo; "; using (var rdr = await SqlCommandWithProgress.ExecuteReader(constr, sql, s => Console.WriteLine(s))) { if (!rdr.IsClosed) { while (rdr.Read()) { Console.WriteLine("Row read"); } } } Console.WriteLine("Hit any key to exit."); Console.ReadKey(); } } 

哪个输出:

 Reads 0, Writes 0, CPU 1061, RowCount 0, WaitTime 0, LastWaitType SOS_SCHEDULER_YIELD, Status running Reads 0, Writes 0, CPU 2096, RowCount 0, WaitTime 0, LastWaitType SOS_SCHEDULER_YIELD, Status running Reads 0, Writes 0, CPU 4553, RowCount 11043136, WaitTime 198, LastWaitType CXPACKET, Status suspended Row read Hit any key to exit. 

这是一个有趣的问题。 我过去不得不实施类似的事情。 在我们的案例中,优先事项是:

  • 如果用户不想留下并等待,请保持客户端响应。
  • 更新操作和进度的用户。

我要做的是使用线程在后台运行该过程,如:

 HostingEnvironment.QueueBackgroundWorkItem(ct => FunctionThatCallsSQLandTakesTime(p, q, s)); 

然后使用一种估计工作时间的方法,我会在时钟上从客户端增加进度条。 为此,查询数据中的变量,使您与FunctionThatCallsSQLandTakesTime所需的工作时间成线性关系。

例如; 本月活跃用户数驱动FunctionThatCallsSQLandTakesTime占用的时间。 每10000个用户需要5分钟。 因此,您可以相应地更新进度条。

我想知道这是否是一个合理的方法:

  IAsyncResult result = cmd2.BeginExecuteNonQuery(); int count = 0; while (!result.IsCompleted) { count++; if (count % 500 == 0) { lblProcessing.Text = "Transactions " + i.ToString(); lblProcessing.Refresh(); } // Wait for 1/10 second, so the counter // does not consume all available resources // on the main thread. System.Threading.Thread.Sleep(100); }