Udostępnij przez


Implementowanie transakcji jawnej przy użyciu funkcji CommittableTransaction

Klasa CommittableTransaction zapewnia jawny sposób używania transakcji przez aplikacje, w przeciwieństwie do niejawnego używania TransactionScope klasy. Jest to przydatne w przypadku aplikacji, które chcą używać tej samej transakcji w wielu wywołaniach funkcji lub wielu wywołań wątków. W przeciwieństwie do klasy, piszący aplikację musi w szczególności wywołać metody TransactionScope i Commit w celu zatwierdzenia lub przerwania transakcji.

Omówienie klasy CommittableTransaction

Klasa CommittableTransaction pochodzi z Transaction klasy, zapewniając w związku z tym wszystkie funkcje tej drugiej. Szczególnie przydatna jest metoda Rollback w klasie Transaction, która może być również używana do wycofania obiektu CommittableTransaction.

Klasa Transaction jest podobna do klasy CommittableTransaction, ale nie oferuje metody Commit. Dzięki temu można przekazać obiekt transakcji (lub klony) do innych metod (potencjalnie w innych wątkach), jednocześnie kontrolując, kiedy transakcja jest zatwierdzana. Wywoływany kod jest w stanie zarejestrować i głosować na transakcję, ale tylko twórca CommittableTransaction obiektu ma możliwość zatwierdzenia transakcji.

Podczas pracy z klasą CommittableTransaction należy zwrócić uwagę na następujące kwestie:

  • CommittableTransaction Utworzenie transakcji nie powoduje ustawienia otoczenia transakcji. Należy w szczególności ustawić i zresetować otoczenia transakcji, aby upewnić się, że menedżerowie zasobów działają w odpowiednim kontekście transakcji. Sposób ustawienia bieżącej transakcji środowiska polega na ustawieniu właściwości statycznej Current na globalnym obiekcie Transaction.

  • Nie można ponownie użyć CommittableTransaction obiektu. CommittableTransaction Po zatwierdzeniu lub wycofaniu obiektu nie można go ponownie użyć w transakcji. Oznacza to, że nie można ustawić go jako bieżącego kontekstu transakcji środowiskowej.

Tworzenie obiektu CommittableTransaction

Poniższy przykład tworzy nową CommittableTransaction i zatwierdza ją.

//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

Utworzenie wystąpienia CommittableTransaction nie powoduje automatycznego ustawienia kontekstu transakcji. W związku z tym żadna operacja w menedżerze zasobów nie jest częścią tej transakcji. Właściwość statyczna Current globalnego obiektu Transaction służy do ustawiania lub pobierania transakcji środowiskowej, a aplikacja musi ją ustawić ręcznie, aby upewnić się, że menedżerowie zasobów mogą brać udział w transakcji. Dobrym rozwiązaniem jest również zapisanie poprzedniej transakcji środowiskowej i przywrócenie jej po zakończeniu korzystania z obiektu CommittableTransaction.

Aby zatwierdzić transakcję, należy jawnie wywołać metodę Commit . W przypadku wycofywania transakcji należy wywołać metodę Rollback . Należy pamiętać, że dopóki element CommittableTransaction nie zostanie zatwierdzony lub wycofany, wszystkie zasoby zaangażowane w tej transakcji są nadal zablokowane.

Obiekt CommittableTransaction może być używany między wywołaniami funkcji i wątkami. Jednak deweloper aplikacji musi obsługiwać wyjątki, a w szczególności wywoływać Rollback(Exception) metodę w przypadku awarii.

Zatwierdzanie asynchroniczne

Klasa CommittableTransaction udostępnia również mechanizm zatwierdzania transakcji asynchronicznie. Zatwierdzenie transakcji może zająć dużo czasu, ponieważ może obejmować wiele dostępu do bazy danych i możliwe opóźnienie sieci. Jeśli chcesz uniknąć blokad w aplikacjach o wysokiej wydajności, możesz skorzystać z asynchronicznego zatwierdzania, aby zakończyć pracę transakcyjną tak szybko, jak to możliwe, i uruchomić operację zatwierdzania jako zadania w tle. Metody BeginCommitEndCommit i CommittableTransaction klasy umożliwiają to.

Możesz wywołać BeginCommit, aby wysłać blokadę zatwierdzenia do wątku z puli wątków. Możesz również wywołać metodę EndCommit , aby określić, czy transakcja została rzeczywiście zatwierdzona. Jeśli transakcja nie udało się zatwierdzić z jakiegokolwiek powodu, EndCommit zgłasza wyjątek dotyczący transakcji. Jeśli transakcja nie zostanie jeszcze zatwierdzona w momencie wywołania EndCommit, wywołujący zostanie zablokowany do momentu zatwierdzenia lub przerwania transakcji.

Najprostszym sposobem wykonania zatwierdzenia asynchronicznego jest podanie metody wywołania zwrotnego, która ma zostać wywołana po zakończeniu zatwierdzania. Należy jednak wywołać metodę EndCommit dla oryginalnego CommittableTransaction obiektu użytego do wywołania . Aby uzyskać ten obiekt, można rzutować w dół parametr IAsyncResult metody zwrotnej, ponieważ klasa CommittableTransaction implementuje klasę IAsyncResult.

Poniższy przykład pokazuje, jak można przeprowadzić asynchroniczne zatwierdzenie.

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  
     }  
}  

Zobacz także