다음을 통해 공유


CommitableTransaction을 사용하여 명시적 트랜잭션 구현

응용 프로그램에서 트랜잭션을 명시적으로 사용하기 위해 CommittableTransaction 클래스를 사용하는 방법을 제공하며, 이는 TransactionScope 클래스를 암시적으로 사용하는 방식과는 다릅니다. 여러 함수 호출 또는 여러 스레드 호출에서 동일한 트랜잭션을 사용하려는 애플리케이션에 유용합니다. TransactionScope 클래스와 달리, 애플리케이션 작성자는 트랜잭션을 커밋하거나 중단하기 위해 CommitRollback 메서드를 구체적으로 호출해야 합니다.

CommittableTransaction 클래스 개요

클래스는 CommittableTransaction 클래스에서 Transaction 파생되므로 후자의 모든 기능을 제공합니다. 특히 Rollback 클래스의 Transaction 메서드는 CommittableTransaction 개체를 롤백하는 데에도 사용할 수 있어 유용합니다.

Transaction 클래스는 클래스와 CommittableTransaction 비슷하지만 메서드를 Commit 제공하지는 않습니다. 이렇게 하면 트랜잭션이 커밋되는 시기를 제어하는 동안 트랜잭션 개체(또는 트랜잭션의 복제본)를 다른 메서드(잠재적으로 다른 스레드에서)에 전달할 수 있습니다. 호출된 코드는 트랜잭션을 등록하고 투표할 수 있지만 개체의 CommittableTransaction 작성자만 트랜잭션을 커밋할 수 있습니다.

CommittableTransaction 클래스로 작업할 때 다음과 같은 사항에 유의해야 합니다.

  • 트랜잭션을 생성해도 환경 트랜잭션이 설정되지 않습니다. 리소스 관리자가 적절한 경우 올바른 트랜잭션 컨텍스트에서 작동하도록 앰비언트 트랜잭션을 구체적으로 설정하고 다시 설정해야 합니다. 현재 앰비언트 트랜잭션을 설정하는 방법은 전역 Current 개체에서 정적 Transaction 속성을 설정하는 것입니다.

  • CommittableTransaction 개체는 재사용할 수 없습니다. 개체가 CommittableTransaction 커밋되거나 롤백되면 트랜잭션에서 다시 사용할 수 없습니다. 즉, 현재 앰비언트 트랜잭션 컨텍스트로 설정할 수 없습니다.

CommittableTransaction을 만들기

다음 샘플에서는 새로 CommittableTransaction 만들어 커밋합니다.

//Create a committable transaction
tx = new CommittableTransaction();

SqlConnection myConnection = new SqlConnection("server=(local)\\SQLExpress;Integrated Security=SSPI;database=northwind");
SqlCommand myCommand = new SqlCommand();

//Open the SQL connection
myConnection.Open();

//Give the transaction to SQL to enlist with
myConnection.EnlistTransaction(tx);

myCommand.Connection = myConnection;

// Restore database to near it's original condition so sample will work correctly.
myCommand.CommandText = "DELETE FROM Region WHERE (RegionID = 100) OR (RegionID = 101)";
myCommand.ExecuteNonQuery();

// Insert the first record.
myCommand.CommandText = "Insert into Region (RegionID, RegionDescription) VALUES (100, 'MidWestern')";
myCommand.ExecuteNonQuery();

// Insert the second record.
myCommand.CommandText = "Insert into Region (RegionID, RegionDescription) VALUES (101, 'MidEastern')";
myCommand.ExecuteNonQuery();

// Commit or rollback the transaction
while (true)
{
    Console.Write("Commit or Rollback? [C|R] ");
    ConsoleKeyInfo c = Console.ReadKey();
    Console.WriteLine();

    if ((c.KeyChar == 'C') || (c.KeyChar == 'c'))
    {
        tx.Commit();
        break;
    }
    else if ((c.KeyChar == 'R') || (c.KeyChar == 'r'))
    {
        tx.Rollback();
        break;
    }
}
myConnection.Close();
tx = null;
tx = New CommittableTransaction

Dim myConnection As New SqlConnection("server=(local)\SQLExpress;Integrated Security=SSPI;database=northwind")
Dim myCommand As New SqlCommand()

'Open the SQL connection
myConnection.Open()

'Give the transaction to SQL to enlist with
myConnection.EnlistTransaction(tx)

myCommand.Connection = myConnection

'Restore database to near it's original condition so sample will work correctly.
myCommand.CommandText = "DELETE FROM Region WHERE (RegionID = 100) OR (RegionID = 101)"
myCommand.ExecuteNonQuery()

'Insert the first record.
myCommand.CommandText = "Insert into Region (RegionID, RegionDescription) VALUES (100, 'MidWestern')"
myCommand.ExecuteNonQuery()

'Insert the second record.
myCommand.CommandText = "Insert into Region (RegionID, RegionDescription) VALUES (101, 'MidEastern')"
myCommand.ExecuteNonQuery()

'Commit or rollback the transaction
Dim c As ConsoleKeyInfo
While (True)
    Console.Write("Commit or Rollback? [C|R] ")
    c = Console.ReadKey()
    Console.WriteLine()

    If (c.KeyChar = "C") Or (c.KeyChar = "c") Then
        tx.Commit()
        Exit While
    ElseIf ((c.KeyChar = "R") Or (c.KeyChar = "r")) Then
        tx.Rollback()
        Exit While
    End If
End While

myConnection.Close()
tx = Nothing

인스턴스 CommittableTransaction 를 만들면 앰비언트 트랜잭션 컨텍스트가 자동으로 설정되지 않습니다. 따라서 리소스 관리자에 대한 모든 작업은 해당 트랜잭션의 일부가 아닙니다. 전역 Current 개체의 정적 Transaction 속성은 앰비언트 트랜잭션을 설정하거나 검색하는 데 사용되며, 애플리케이션은 리소스 관리자가 트랜잭션에 참여할 수 있도록 수동으로 설정해야 합니다. 이전 앰비언트 트랜잭션을 저장해 두었다가 CommittableTransaction 객체 사용을 마치면 복원하는 것도 좋은 방법입니다.

트랜잭션을 커밋하려면 메서드를 명시적으로 호출 Commit 해야 합니다. 트랜잭션을 롤백하려면 메서드를 Rollback 호출해야 합니다. 커밋되거나 롤백될 때까지 CommittableTransaction 해당 트랜잭션에 관련된 모든 리소스가 여전히 잠겨 있다는 점에 유의해야 합니다.

CommittableTransaction 함수 호출 및 스레드에서 개체를 사용할 수 있습니다. 그러나 예외를 처리하고 특히 오류가 발생할 경우 메서드를 호출 Rollback(Exception) 하는 것은 애플리케이션 개발자의 맡입니다.

비동기 커밋

또한 클래스는 CommittableTransaction 트랜잭션을 비동기적으로 커밋하는 메커니즘을 제공합니다. 트랜잭션 커밋에는 여러 데이터베이스 액세스 및 가능한 네트워크 대기 시간이 포함될 수 있으므로 상당한 시간이 걸릴 수 있습니다. 높은 처리량 애플리케이션에서 교착 상태를 방지하려면 비동기 커밋을 사용하여 가능한 한 빨리 트랜잭션 작업을 완료하고 커밋 작업을 백그라운드 작업으로 실행할 수 있습니다. BeginCommit 클래스의 EndCommit 메서드와 CommittableTransaction 메서드는 이 작업을 수행하는 것을 가능하게 합니다.

BeginCommit을 호출하여 커밋 대기를 스레드 풀에서 스레드로 디스패치할 수 있습니다. 호출 EndCommit 하여 트랜잭션이 실제로 커밋되었는지 확인할 수도 있습니다. 어떤 이유로 EndCommit 든 트랜잭션을 커밋하지 못하면 트랜잭션 예외가 발생합니다. 호출될 때까지 EndCommit 트랜잭션이 아직 커밋되지 않은 경우 트랜잭션이 커밋되거나 중단될 때까지 호출자가 차단됩니다.

비동기 커밋을 수행하는 가장 쉬운 방법은 커밋이 완료될 때 호출할 콜백 메서드를 제공하는 것입니다. 그러나 호출을 호출하는 EndCommit 데 사용되는 원래 CommittableTransaction 개체에서 메서드를 호출해야 합니다. 해당 객체를 얻으려면 클래스가 CommittableTransaction 클래스를 구현하므로 콜백 메서드의 IAsyncResult 매개변수를 다운캐스트할 수 있습니다.

다음 예제에서는 비동기 커밋을 수행하는 방법을 보여줍니다.

public void DoTransactionalWork()  
{  
     Transaction oldAmbient = Transaction.Current;  
     CommittableTransaction committableTransaction = new CommittableTransaction();  
     Transaction.Current = committableTransaction;  
  
     try  
     {  
          /* Perform transactional work here */  
          // No errors - commit transaction asynchronously  
          committableTransaction.BeginCommit(OnCommitted,null);  
     }  
     finally  
     {  
          //Restore the ambient transaction
          Transaction.Current = oldAmbient;  
     }  
}  
void OnCommitted(IAsyncResult asyncResult)  
{  
     CommittableTransaction committableTransaction;  
     committableTransaction = asyncResult as CommittableTransaction;
     Debug.Assert(committableTransaction != null);  
     try  
     {  
          using(committableTransaction)  
          {  
               committableTransaction.EndCommit(asyncResult);  
          }  
     }  
     catch(TransactionException e)  
     {  
          //Handle the failure to commit  
     }  
}  

참고하십시오