我有一个长时间运行的存储过程,它执行繁重的计算并更新数千条记录,在内部处理事务以保持每条记录的一致性,同时最小化阻塞并允许查询进程进度。
我正在研究SQL Server 2008,.Net Framework 4和VB 2010
我尝试通过SqlCommand.BeginExecuteNonQuery从我的逻辑层异步调用此SP,传递此方法回调以执行一些清理并记录命令成功完成或失败的两种情况。然而,在SP真正结束之前调用回调函数,AsyncResult.IsCompleted = true,因此,当此回调执行SqlCommand.EndExecuteNonQuery时,它会挂起等待命令完成或超时到期。
我阅读了很多文章,但到目前为止,这个模式看起来非常简单,而且无论在哪里,只有在命令完成时才会触发回调。
简单地说,我的代码基本上是这样的:
Public Shared Sub ExecAsync(sp_call As String, async_connection As String)
'...
Dim cmd As SqlClient.SqlCommand = New SqlClient.SqlCommand(sp_call, New SqlClient.SqlConnection(async_connection))
Dim callback As New AsyncCallback(AddressOf CallbackAsync)
cmd.BeginExecuteNonQuery(callback, cmd)
'...
'try catch ommited
End Sub
Public Shared Sub CallbackAsync(ByVal result As IAsyncResult)
Dim cmd As SqlClient.SqlCommand = TryCast(result.AsyncState, SqlClient.SqlCommand)
cmd.EndExecuteNonQuery(result)
If cmd.Connection IsNotNothing Then
cmd.Connection.Close()
End If
'...
'try catch and logging ommited
End Sub
我可以用这个简单的脚本模拟真实SP的行为:
declare @i int
declare @iId int
select @iId = 1
select @i = 0
delete xxx_xxx --trivial test table
where id = @iId
while @i < 100
begin
begin transaction
insert into xxx_xxx (id, hora, iNum) values (@iId, getdate(), @i)
commit transaction
select @i += 1
WAITFOR DELAY '00:00:01'
end
在我做的许多尝试中,我发现如果我删除SQL脚本中的事务,行为就是预期的,并且回调会在执行结束时触发。 执行回调在执行结束之前触发的事务,将它们注释掉,最后触发它。
不幸的是,避免交易不是真实场景中的一种选择。
是否有任何理由,或者更确切地说,有一些方法可以避免它,并且在SP结束后实际调用回调函数?
任何帮助将不胜感激。 提前谢谢。
Gustavo Sosa