【问题标题】:Using TransactionScope around a stored procedure with transaction in SQL Server 2014在 SQL Server 2014 中使用带有事务的存储过程周围的 TransactionScope
【发布时间】:2015-07-15 04:53:14
【问题描述】:

我正在使用 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

【问题讨论】:

    标签: c# sql-server stored-procedures ado.net transactionscope


    【解决方案1】:

    是的,TransactionScope 在包装 TSQL BEGIN / COMMIT TRANSACTION 或 ADO SqlConnection.BeginTransaction 时仍然可以工作。包装单个连接时,其行为类似于在Sql 中嵌套事务:

    • @@TranCount 将在每个BEGIN TRAN 上递增

    • COMMIT TRAN 将简单地减少 @@TRANCOUNT。只有@@TRANCOUNT 达到零时才会提交事务。

    但是:

    • ROLLBACK TRAN 将中止整个事务(即@@TRANCOUNT to zero),除非您使用Save Points(即SAVE TRANSACTION xx ... ROLLBACK TRANSACTION xx
    • 使用存储过程时,如果退出 SPROC 时连接的 @@TRANCOUNT 与进入 SPROC 时的值不同,您将收到错误消息。

    因此,将事务语义留给 TransactionScope 并从混乱的 TSQL 中删除任何手动的 BEGIN TRAN / COMMIT TRAN 逻辑通常要容易得多。

    编辑 - 澄清下面的 cmets

    因此,我建议将 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;
    

    【讨论】:

    • 感谢您的详细回答。您的意思是如果在存储过程中调用了“rollback tran”,那么 TransactionScope 将自动回滚整个事务而不仅仅是存储过程事务,即使该过程没有引发错误?
    • 是的,在同一连接上从 TSQL 调用 ROLLBACK TRAN 将撤消在 Conn 上完成的任何其他工作,例如由 ADO。只有使用 SAVEPOINT 才能限制 ROLLBACK 的范围。但是请注意,SavePoints 不适用于分布式事务,这可能会在 TransactionScope 中非常微妙地发生,例如如果您同时在范围内打开多个 conn。我发现将BEGIN/COMMIT TRAN 保留在 .Net 应用程序调用的 SPROC 中的唯一原因是 SPROC 是否还需要在其他地方执行,例如来自 SSMS 的 ad-hoc。
    • 但是,存储过程中的rollback tran不会回滚TransactionScope事务,除非在调用rollback tran语句时存储过程也引发错误?
    • 不,ROLLBACK 将终止 outermost 事务。我相信您正在寻找具有标准模式implementation heresave points。如您所见,逻辑更加复杂,因此建议避免使用保存点进行部分回滚,而是从.Net 控制事务。也可以使用SavePoints via SqlClient
    • 好的。我知道了。我猜 TransactionScope 对象正在跟踪数据库连接是否已回滚事务,因此您所说的会发生。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-14
    • 2018-02-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-14
    相关资源
    最近更新 更多