1. 程式人生 > >.net 事務巢狀儲存過程事務的處理方法

.net 事務巢狀儲存過程事務的處理方法

網上有很多這樣的問題分析,無非以下兩種情況:

1、begin tran 後沒有rollback tran 或commit tran

2、應用程式資料庫連線事務不在一個會話中;

但以後我要說的情況有點特別,先說一下場景:

1、.net 開發環境

2、使用sqlconnection物件發起事務

3、事務內有多個儲存過程

4、各儲存過程內有自已的事務

總之就是sqlconnection事務巢狀儲存過程事務,以下是演示程式碼,先看看這樣做會有什麼問題

以下是VB.NET程式碼,什麼語言不重要,只要是用.net 環境和SqlConnection物件就符合條件

'事務巢狀測試
    Sub testtrans()
        Dim mycn As SqlConnection
        Dim tran As SqlTransaction
        Dim inputs As New Dictionary(Of String, Object)
        Dim outputs As New Dictionary(Of String, SqlParameter)
        mycn = obj.GetSqlConnection
        obj.openSqlConnection(mycn)
        Dim errMsg As String
        Dim cmd As SqlCommand
        Try
            tran = mycn.BeginTransaction
            '儲存過程輸入引數
            inputs.Add("@test", 1)
            '儲存過程輸出引數
            outputs.Add("@result", New SqlParameter("@result", SqlDbType.VarChar, 10))
            '執行這事出錯,提示EXECUTE 後的事務計數指示 BEGIN 和 COMMIT 
            '             語句的數目不匹配。上一計數 = 1,當前計數 = 0。

            obj.execProc("test_pro1", inputs, outputs, errMsg, tran)<span style="white-space:pre">	</span>'自定義的執行儲存過程的方法
            If tran.Connection IsNot Nothing Then
                tran.Commit()
            End If
        Catch ex As Exception
            If tran.Connection IsNot Nothing Then
                tran.Rollback()
            End If
            Me.callBack(False, ex.Message)
            Exit Sub
        Finally
            obj.closeSqlConnection(mycn)
        End Try

以下是儲存過程
ALTER PROCEDURE [dbo].[test_pro1]
@test INT,
@result VARCHAR(10) OUTPUT
AS
BEGIN TRANSACTION 
  SET @result='A'
 DECLARE @aa INT
 set @aa=5/0
ROLLBACK TRANSACTION 
通過.net事務呼叫含事務的儲存過程,只要儲存過程觸發rollback tranaction,會馬上觸發.net事務異常,即

---------------------

EXECUTE 後的事務計數指示 BEGIN 和 COMMIT 語句的數目不匹配。上一計數 = 1,當前計數 = 0。

---------------------

這樣的異常,但實際上儲存過程是按要求執行的,返回引數也返回預期值,也正確回滾了;

如何處理這種問題呢?比較簡單,只要在catch對錯誤碼準確判斷即可,如下程式碼:

    cmd.ExecuteNonQuery()
        Catch ex As SqlException
            If ex.Number <> 266 Then
                errMsg = "執行" & procName & "出錯,錯誤碼:" & errcode
                WriteLog(errMsg & ",詳情:" & Chr(13) & ex.Message & Chr(13) & ex.StackTrace)
            End If
266是事務計數器異常程式碼,是這個程式碼直接當正常即可,因為儲存過程引數返回到上一級方法後,再判斷引數值是否正確,若正常就繼續,不正常就退出方法,當然最上一級sqlconnection要對事務做一下判斷 
Catch ex As Exception
            If _tran IsNot Nothing Then
                If _tran.Connection IsNot Nothing Then
                    _tran.Rollback()
                End If
            End If
            obj.WriteLog("XX,錯誤程式碼:" & errnum & Chr(13) & ex.StackTrace)
            Return obj.CallBack(False, "XX,錯誤程式碼:" & errnum)
        Finally
            obj.closeSqlConnection(cn)
        End Try
注:sqlconnection的事務物件sqlTransaction,要用引用傳值方式給內部執行儲存過程的sqlCommand物件的transaction屬性;

另外,我認為.net完全沒有必要觸發異常,.net的事務是要提交還是回滾,.net本來就提供了判斷事務是否有效的方法,如果有效且沒有其他異常,手動提交,反之回滾;