如何 运行 .Net: SQLCommand 在它自己的线程上允许用户安全地取消它 - 运行

How to run a .Net: SQLCommand on it's own thread with the ability to allow the user to safely cancel it mid-run

我和我的团队整个星期都在为此苦思冥想。感谢任何帮助。我们正在使用 VB.Net。我们有一个表单,用户将在其中使用 SQLCommand 来执行可能需要几分钟到 运行 的存储过程。我们希望用户能够在 运行ning 期间继续在应用程序中工作,或者能够在处理过程中取消请求。我们研究了线程和后台工作者,以及异步处理。我们无法找到一种解决方案,使我们能够安全无误地取消存储过程和线程的 运行ning。有谁知道如何安全地做到这一点。这是我们目前所拥有的。 *为了安全和尺寸删除了一些细节

Private Sub frmAdhocRptGenerator_Load(sender As Object, e As EventArgs) Handles Me.Load
    BackGroundWorkerAdhoc.WorkerReportsProgress = True
    BackGroundWorkerAdhoc.WorkerSupportsCancellation = True
End Sub

Private Sub btnExecute_Click(sender As Object, e As EventArgs) Handles btnExecute.Click
        If Not BackGroundWorkerAdhoc.IsBusy = True Then BackGroundWorkerAdhoc.RunWorkerAsync()
End Sub

Private Sub BackGroundWorkerAdhoc_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackGroundWorkerAdhoc.DoWork
    Try
        If BackGroundWorkerAdhoc.CancellationPending = True Then
            e.Cancel = True
            Exit Sub
        End If
        Load_n_ExecuteProc(tmpConn)
    Catch ex As Exception
        ErrorHandler("frmAdhocRptGenerator.subBackGroundWorkerAdhoc_DoWork", ex.ToString)
        tmpConn.Close()
    End Try
End Sub

Private Function Load_n_ExecuteProc(theConn As SqlConnection) As Boolean
    If BackGroundWorkerAdhoc.CancellationPending = True Then
        Return False
        Exit Function
    End If
    Try
        Dim RunCompleted As Boolean = False

        cmdPROVIDER_FINDER = New SqlCommand("SP$PROVIDER_FINDER", tmpConn)
                cmdPROVIDER_FINDER.CommandType = CommandType.StoredProcedure
                cmdPROVIDER_FINDER.CommandTimeout = 10000
                Dim rst As Integer = cmdPROVIDER_FINDER.ExecuteNonQuery()
                RunCompleted = rst > -1
        Return RunCompleted
    Catch ex As Exception
        ErrorHandler("frmAdhocRptGenerator.Load_n_ExecuteProc", ex.ToString)
        Return False
    End Try
End Function


Private Sub BackGroundWorkerAdhoc_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackGroundWorkerAdhoc.RunWorkerCompleted
    Try
        If Success = True Then
            Dim filename As String = "AdhocResults" & Now.Month.ToString & "-" & Now.Day.ToString & "-" & Now.Year.ToString
            Export2Excel("SELECT * FROM ##PF_PROVIDERS" & currUser.UserID, filename, True)
        End If
        tmpConn.Close()
        Success = False
    Catch ex As Exception
        ErrorHandler("frmAdhocRptGenerator.BackGroundWorkerAdhoc_RunWorkerCompleted", ex.ToString)
    Finally
        tmpConn.Close()
    End Try
End Sub


Private Sub btnCancel_Click(sender As Object, e As EventArgs) Handles btnCancel.Click
    If BackGroundWorkerAdhoc.IsBusy = True Then
        BackGroundWorkerAdhoc.CancelAsync()
        cmdPROVIDER_FINDER.Cancel()
        tmpConn.Close()
        tmpConn = Nothing
    End If
    btnCancel.Enabled = False
End Sub

后台工作者取消并不是什么神奇的方法。它所做的只是将 .CancellationPending 设置为 true。由您的代码以安全的方式实际执行取消,然后退出 DoWork 处理程序。

您现有的取消代码只有在您的用户在线程开始之前取消时才有效,这不太可能,因此您可以将其删除。

If BackGroundWorkerAdhoc.CancellationPending = True Then
    Return False
    Exit Function
End If

而是从您的 UI 线程发出 SqlCommand.Cancel 调用。然后该命令将终止,然后您将照常退出 DoWork。如果您要过早地终止 sql 命令,我建议您在丢弃的 SqlConnection 上执行此操作,因此我创建了一个新命令。

Private Sub BackGroundWorkerAdhoc_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackGroundWorkerAdhoc.DoWork
    Try
        Using myNewConn As New SqlConnection(...)
            Load_n_ExecuteProc(myNewConn)
        End Using
    Catch ex As Exception
        ErrorHandler("frmAdhocRptGenerator.subBackGroundWorkerAdhoc_DoWork", ex.ToString)
    End Try
End Sub

Private Function Load_n_ExecuteProc(theConn As SqlConnection) As Boolean
    Try
        cmdPROVIDER_FINDER = New SqlCommand("SP$PROVIDER_FINDER", tmpConn)
        cmdPROVIDER_FINDER.CommandType = CommandType.StoredProcedure
        cmdPROVIDER_FINDER.CommandTimeout = 10000
        Dim rst As Integer = cmdPROVIDER_FINDER.ExecuteNonQuery()
        return = rst > -1
    Catch ex As Exception
        ErrorHandler("frmAdhocRptGenerator.Load_n_ExecuteProc", ex.ToString)            
    Finally
        cmdPROVIDER_FINDER = Nothing
    End Try
End Function

Public Sub CancelSqlCommand()
    Dim cmd = cmdPROVIDER_FINDER
    If cmd isnot Nothing cmd.Cancel()
End Sub