簡體   English   中英

在SQL Server 2014中使用TransactionScope圍繞存儲過程與事務

[英]Using TransactionScope around a stored procedure with transaction in SQL Server 2014

我正在使用C#和ADO.Net與TransactionScope在ASP.Net應用程序中運行事務。 此事務應該在多個表中保存一些數據,然后向訂閱者發送電子郵件。

問題 :當它包含對SQL Server 2014中具有自己的事務的存儲過程的調用時,它是否是對TransactionScope的有效使用,或者是否應該從存儲中刪除SQL事務語句,即begin trancommit tranrollback tran語句在此TransactionScope中調用的過程?

下面提到了此場景的C#代碼以及存儲過程的T-SQL代碼。

使用TransactionScope C#代碼:

  try 
    {
        using (TransactionScope scope = new TransactionScope())
        {
            using (SqlConnection connection1 = new SqlConnection(connectString1))
            {
                // Opening the connection automatically enlists it in the  
                // TransactionScope as a lightweight transaction.
                connection1.Open();

                // SaveEmailData is a stored procedure that has a transaction within it
                SqlCommand command1 = new SqlCommand("SaveEmailData", connection1);
                command1.CommandType = CommandType.StoredProcedure;
                command1.ExecuteNonQuery();

            }

            //Send Email using the helper method
            EmailHelper.SendCustomerEmails(customerIds);

            // The Complete method commits the transaction. If an exception has been thrown, 
            // Complete is not  called and the transaction is rolled back.
            scope.Complete();

        }
    }
    catch( Exception ex)
    {
       Logger.Log(ex);
    }

存儲過程的T-SQL SaveEmailData

SET NOCOUNT ON

    BEGIN TRY
        DECLARE @emailToUserId BIGINT

        BEGIN TRAN
        -- //update statement. detail statement omitted
        UPDATE TABLE1...

         --update statement. detail statement omitted
        UPDATE TABLE2...

        IF @@trancount > 0
        BEGIN
            COMMIT TRAN
        END
    END TRY

    BEGIN CATCH

        IF @@TRANCOUNT > 0
        BEGIN
            ROLLBACK TRAN
        END

        EXEC Error_RaiseToADONET

    END CATCH

是的,在包裝TSQL BEGIN / COMMIT TRANSACTION或ADO SqlConnection.BeginTransaction時, TransactionScope仍然可以工作。 包裝單個連接時,行為類似於在Sql嵌套事務:

  • @@TranCount將在每個BEGIN TRAN上遞增

  • COMMIT TRAN將簡單地減少@@TRANCOUNT 只有在@@TRANCOUNT為零時才會提交交易。

然而:

  • ROLLBACK TRAN將中止整個事務(即@@ TRANCOUNT為零 ),除非您使用保存點 (即SAVE TRANSACTION xx ... ROLLBACK TRANSACTION xx
  • 使用存儲過程時,如果連接的@@TRANCOUNT在退出SPROC時與輸入SPROC時的值不同,則會收到錯誤。

因此,通常更容易將事務語義留給TransactionScope並刪除任何手動BEGIN TRAN / COMMIT TRAN邏輯,使您的TSQL混亂。

編輯 - 澄清以下評論

  • 在OP的情況下,SPROC沒有考慮嵌套事務(即,是否由Sql或.Net外部事務包裝),具體地說, BEGIN CATCH塊中的ROLLBACK將中止整個外部事務並且可能會進一步導致由於未遵守@@TRANCOUNT規則,因此外部TransactionScope錯誤。 如果SPROC需要以嵌套或獨立事務方式運行,則應遵循此類嵌套事務模式

  • SavePoints不適用於分布式事務 ,並且TransactionScope可以輕松升級到分布式事務,例如,如果您在事務范圍內使用不同的連接字符串或控制其他資源。

因此,我建議將PROC重構為一個“快樂”的核心/內部案例,從事務范圍調用此內部proc,並在那里進行任何異常處理和回滾。 如果你還需要從Ad Hoc Sql調用proc,那么提供一個外部包裝器Proc,它具有異常處理:

-- Just the happy case. This is called from .Net TransactionScope
CREATE PROC dbo.InnerNonTransactional
  AS
    BEGIN 
      UPDATE TABLE1...
      UPDATE TABLE2 ....
    END;

-- Only needed if you also need to call this elsewhere, e.g. from AdHoc Sql
CREATE PROC dbo.OuterTransactional
  AS
    BEGIN
      BEGIN TRY
        BEGIN TRAN
            EXEC dbo.InnerNonTransactional
        COMMIT TRAN
      END TRY
      BEGIN CATCH
         -- Rollback and handling code here.
      END CATCH
    END;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM