c# – 使用CancellationToken取消SQL Server查询
我在SQL Server中有一个长时间运行的存储过程,我的用户需要能够取消.我写了一个小测试应用程序如下,表明SqlCommand.Cancel()方法工作相当不错:
private SqlCommand cmd; private void TestSqlServerCancelSprocExecution() { TaskFactory f = new TaskFactory(); f.StartNew(() => { using (SqlConnection conn = new SqlConnection("connStr")) { conn.InfoMessage += conn_InfoMessage; conn.FireInfoMessageEventOnUserErrors = true; conn.Open(); cmd = conn.CreateCommand(); cmd.CommandType = CommandType.StoredProcedure; cmd.CommandText = "dbo.[CancelSprocTest]"; cmd.ExecuteNonQuery(); } }); } private void cancelButton_Click(object sender,EventArgs e) { if (cmd != null) { cmd.Cancel(); } } 调用cmd.Cancel()后,我可以验证底层存储过程是否基本上立即停止执行.鉴于我在应用程序中使用了异步/等待模式,我希望SqlCommand上使用CancellationToken参数的异步方法工作同样顺利.不幸的是,我发现在CancellationToken上调用Cancel()使InfoMessage事件处理程序不再被调用,但是底层存储过程继续执行.我的异步版本的测试代码如下: private SqlCommand cmd; private CancellationTokenSource cts; private async void TestSqlServerCancelSprocExecution() { cts = new CancellationTokenSource(); using (SqlConnection conn = new SqlConnection("connStr")) { conn.InfoMessage += conn_InfoMessage; conn.FireInfoMessageEventOnUserErrors = true; conn.Open(); cmd = conn.CreateCommand(); cmd.CommandType = CommandType.StoredProcedure; cmd.CommandText = "dbo.[CancelSprocTest]"; await cmd.ExecuteNonQueryAsync(cts.Token); } } private void cancelButton_Click(object sender,EventArgs e) { cts.Cancel(); } 我错过了CancellationToken应该如何工作吗?我在.NET 4.5.1和SQL Server 2012,如果重要. 编辑:我重新编写测试应用程序作为控制台应用程序,以防同步上下文是一个因素,我看到相同的行为 – 调用CancellationTokenSource.Cancel()不会停止执行底层的存储过程. 编辑:这是我所要求的存储过程的正文.它以一秒的间隔插入记录并打印结果,以便轻松查看取消尝试是否立即生效. WHILE (@loop <= 40) BEGIN DECLARE @msg AS VARCHAR(80) = 'Iteration ' + CONVERT(VARCHAR(15),@loop); RAISERROR (@msg,1) WITH NOWAIT; INSERT INTO foo VALUES (@loop); WAITFOR DELAY '00:00:01.01'; SET @loop = @loop+1; END; 解决方法
在查看您的存储过程正在做什么后,似乎阻止了取消.
如果你改变 RAISERROR (@msg,1) WITH NOWAIT; 要删除WITH NOWAIT子句,则取消按预期工作.但是,这样可以防止InfoMessage事件实时触发. 您可以以其他方式跟踪长时间运行的存储过程的进度,或注册令牌取消,并调用cmd.Cancel(),因为您知道这是有效的. 另外需要注意的是,使用.NET 4.5,您可以使用Task.Run而不是实例化一个TaskFactory. 所以这里是一个工作的解决方案: private CancellationTokenSource cts; private async void TestSqlServerCancelSprocExecution() { cts = new CancellationTokenSource(); try { await Task.Run(() => { using (SqlConnection conn = new SqlConnection("connStr")) { conn.InfoMessage += conn_InfoMessage; conn.FireInfoMessageEventOnUserErrors = true; conn.Open(); var cmd = conn.CreateCommand(); cts.Token.Register(() => cmd.Cancel()); cmd.CommandType = CommandType.StoredProcedure; cmd.CommandText = "dbo.[CancelSprocTest]"; cmd.ExecuteNonQuery(); } }); } catch (SqlException) { // sproc was cancelled } } private void cancelButton_Click(object sender,EventArgs e) { cts.Cancel(); } 在我的测试中,我不得不将ExecuteNonQuery包装在一个任务中,以使cmd.Cancel()工作.如果我使用ExecuteNonQueryAsync,即使没有传递一个令牌,那么系统会阻塞cmd.Cancel().我不知道为什么会这样,但是将同步方法包装在一个任务中提供了类似的用法. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |