Поделиться через


Доступ к текущей транзакции

Область применения:SQL Server

Если транзакция активна в точке, в которой вводится код среды CLR, запущенный в SQL Server, транзакция предоставляется через класс System.Transactions.Transaction. Свойство Transaction.Current используется для доступа к текущей транзакции. В большинстве случаев доступ к транзакции не требуется явным образом. Для подключений к базе данных ADO.NET автоматически проверяет Transaction.Current при вызове метода Connection.Open и прозрачно заверяет соединение в этой транзакции (если для ключевого слова 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.

Это исключение также ожидается, и для продолжения выполнения необходимо иметь блок try/catch вокруг инструкции Transact-SQL, которая выполняет действие, которое запускает триггер. Несмотря на два исключения, транзакция откатывается и изменения не фиксируются.

Пример

Следующий код является примером отката транзакции из управляемой процедуры с помощью метода Transaction.Rollback. Обратите внимание, что блок try/catch вокруг метода Transaction.Rollback в управляемом коде. Скрипт 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