访问当前事务

适用范围:SQL Server

如果事务在输入 SQL Server 上运行的公共语言运行时 (CLR) 代码时处于活动状态,则事务将通过 System.Transactions.Transaction 类公开。 Transaction.Current 属性用于访问当前事务。 在大多数情况下,不需要显式访问事务。 对于数据库连接,ADO.NET 在调用 Connection.Open 方法时自动检查 Transaction.Current,并在该事务中以透明方式登记连接(除非连接字符串中 Enlist 关键字设置为 false)。

你可能希望在以下方案中直接使用 Transaction 对象:

  • 如果要登记未执行自动登记的资源,或者出于某种原因未在初始化期间登记。

  • 如果您要显式登记事务中的资源。

  • 如果您要从存储过程或函数的内部终止外部事务。 在这种情况下,使用 TransactionScope。 例如,以下代码回滚当前事务:

    using(TransactionScope transactionScope = new TransactionScope(TransactionScopeOptions.Required)) { }
    

本文的其余部分介绍了取消外部事务的其他方法。

取消外部事务

可以用以下方式从托管过程或函数取消外部事务:

  • 通过使用输出参数,托管过程或函数可以返回值。 调用 Transact-SQL 过程可以检查返回的值,并根据需要执行 ROLLBACK TRANSACTION

  • 托管过程或函数可以引发自定义异常。 调用 Transact-SQL 过程可以捕获托管过程或函数在 try/catch 块中引发的异常,并执行 ROLLBACK TRANSACTION

  • 如果满足特定条件,托管过程或函数可以通过调用 Transaction.Rollback 方法来取消当前事务。

当在托管过程或函数中调用 Transaction.Rollback 方法时,它会引发异常并显示不明确的错误消息,并且可以包装在 try/catch 块中。 错误消息类似于以下输出:

Msg 3994, Level 16, State 1, Procedure uspRollbackFromProc, Line 0
Transaction is not allowed to roll back inside a user defined routine, trigger or aggregate because the transaction is not started in that CLR level. Change application logic to enforce strict transaction nesting.

应出现该异常,而且需要 try/catch 块才能继续执行代码。 如果没有 try/catch 块,异常将立即引发到调用 Transact-SQL 过程并完成托管代码执行。 执行完托管代码时,将引发另一个异常:

Msg 3991, Level 16, State 1, Procedure uspRollbackFromProc, Line 1
The context transaction which was active before entering user defined routine, trigger or aggregate " uspRollbackFromProc " has been ended inside of it, which is not allowed. Change application logic to enforce strict transaction nesting. The statement has been terminated.

此异常也是预期的,为了继续执行,必须在执行触发触发器的操作的 Transact-SQL 语句周围设置 try/catch 块。 尽管引发了两个异常,但事务会回滚,并且不会提交更改。

示例

以下代码是使用 Transaction.Rollback 方法从托管过程回滚的事务的示例。 请注意托管代码中 Transaction.Rollback 方法周围的 try/catch 块。 Transact-SQL 脚本创建程序集和托管存储过程。 EXEC uspRollbackFromProc 语句包装在 try/catch 块中,以便捕获托管过程完成执行时引发的异常。

using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Transactions;

public partial class StoredProcedures
{
[Microsoft.SqlServer.Server.SqlProcedure]
public static void uspRollbackFromProc()
{
   using (SqlConnection connection = new SqlConnection(@"context connection=true"))
   {
      // Open the connection.
      connection.Open();

      bool successCondition = true;

      // Success condition is met.
      if (successCondition)
      {
         SqlContext.Pipe.Send("Success condition met in procedure.");
         // Perform other actions here.
      }

      //  Success condition is not met, the transaction will be rolled back.
      else
      {
         SqlContext.Pipe.Send("Success condition not met in managed procedure. Transaction rolling back...");
         try
         {
               // Get the current transaction and roll it back.
               Transaction trans = Transaction.Current;
               trans.Rollback();
         }
         catch (SqlException ex)
         {
            // Catch the expected exception.
            // This allows the connection to close correctly.
         }
      }

      // Close the connection.
      connection.Close();
   }
}
};

在 Transact-SQL 中注册和执行程序集

  1. 注册程序集。

    CREATE ASSEMBLY TestProcs
        FROM 'C:\Programming\TestProcs.dll';
    GO
    
    CREATE PROCEDURE uspRollbackFromProc
    AS EXTERNAL NAME TestProcs.StoredProcedures.uspRollbackFromProc;
    GO
    
  2. 运行该过程。

    BEGIN TRY
        BEGIN TRANSACTION;
    
        -- Perform other actions.
        EXECUTE uspRollbackFromProc;
    
        -- Perform other actions.
        PRINT N'Commiting transaction...';
    
        COMMIT TRANSACTION;
    END TRY
    BEGIN CATCH
        SELECT ERROR_NUMBER() AS ErrorNum,
               ERROR_MESSAGE() AS ErrorMessage;
        PRINT N'Exception thrown, rolling back transaction.';
        ROLLBACK;
        PRINT N'Transaction rolled back.';
    END CATCH
    GO
    
  3. 清理环境。

    DROP PROCEDURE uspRollbackFromProc;
    GO
    
    DROP ASSEMBLY TestProcs;
    GO