Sdílet prostřednictvím


Integrace System.Transactions s SQL Serverem

Rozhraní .NET Framework verze 2.0 zavedlo transakční rozhraní, ke kterému lze přistupovat prostřednictvím System.Transactions oboru názvů. Tato architektura zveřejňuje transakce způsobem, který je plně integrovaný v rozhraní .NET Framework, včetně ADO.NET.

Kromě vylepšení System.Transactions programovatelnosti a ADO.NET může spolupracovat na koordinaci optimalizací při práci s transakcemi. Propagační transakce je jednoduchá (místní) transakce, která lze automaticky zvýšit na plně distribuovanou transakci podle potřeby.

Počínaje ADO.NET 2.0 System.Data.SqlClient podporuje propagační transakce při práci s SQL Serverem. Propagační transakce nevyvolává přidanou režii distribuované transakce, pokud není vyžadována přidaná režie. Propagační transakce jsou automatické a nevyžadují žádný zásah od vývojáře.

Promotable transactions are available only when you use the .NET Framework Zprostředkovatel dat for SQL Server (SqlClient) with SQL Server.

Vytváření propagačních transakcí

Zprostředkovatel rozhraní .NET Framework pro SQL Server poskytuje podporu pro propagační transakce, které jsou zpracovávány prostřednictvím tříd v oboru názvů rozhraní .NET Framework System.Transactions . Propagační transakce optimalizují distribuované transakce odložením vytvoření distribuované transakce, dokud ji nepotřebujete. Pokud je vyžadován pouze jeden správce prostředků, nedojde k žádné distribuované transakci.

Poznámka:

V částečně důvěryhodném scénáři se vyžaduje, DistributedTransactionPermission když je transakce povýšena na distribuovanou transakci.

Scénáře propagačních transakcí

Distribuované transakce obvykle spotřebovávají významné systémové prostředky, které spravuje koordinátor distribuovaných transakcí (MS DTC), který integruje všechny správce prostředků, ke kterým přistupují v transakci. Propagační transakce je speciální forma System.Transactions transakce, která efektivně deleguje práci na jednoduchou transakci SQL Serveru. System.Transactions, System.Data.SqlClienta SQL Server koordinuje práci zapojenou do zpracování transakce, povýšení na úplnou distribuovanou transakci podle potřeby.

Výhodou použití propagačních transakcí je, že když je připojení otevřeno pomocí aktivní TransactionScope transakce a nejsou otevřena žádná další připojení, transakce se potvrdí jako jednoduchá transakce, místo aby došlo k dodatečné režii plné distribuované transakce.

Klíčová slova připojovacího řetězce

Vlastnost ConnectionString podporuje klíčové slovo, které označuje, Enlistzda System.Data.SqlClient bude rozpoznát transakční kontexty a automaticky zařadí připojení do distribuované transakce. Pokud Enlist=trueje připojení automaticky zařazeno do aktuálního kontextu transakce v počátečním vlákně. Pokud Enlist=falsepřipojení SqlClient nepracuje s distribuovanou transakcí. Výchozí hodnota je Enlist true. Pokud Enlist není zadán v připojovací řetězec, připojení je automaticky zařazeno do distribuované transakce, pokud je zjištěn při otevření připojení.

Klíčová Transaction Binding slova v SqlConnection připojovací řetězec řídí přidružení připojení k vložené System.Transactions transakci. Je k dispozici také prostřednictvím TransactionBinding majetku SqlConnectionStringBuilder.

Následující tabulka popisuje možné hodnoty.

Klíčové slovo Popis
Implicitní zrušení vazby Výchozí nastavení Připojení se odpojí od transakce po jejím ukončení a přepne zpět do režimu automatického dokončování.
Explicitní zrušení vazby Připojení zůstává připojeno k transakci, dokud transakce nebude uzavřena. Připojení selže, pokud přidružená transakce není aktivní nebo neodpovídá Current.

Použití TransactionScope

Třída TransactionScope vytvoří transakční blok kódu implicitně zařazením připojení do distribuované transakce. Před opuštěním Complete bloku je nutné volat metodu TransactionScope na konci bloku. Ponechání bloku vyvolá metodu Dispose . Pokud byla vyvolána výjimka, která způsobí, že kód opustí obor, transakce je považována za přerušenou.

Doporučujeme použít blok, abyste měli jistotu using , že Dispose se při ukončení bloku použití volá TransactionScope objekt. Selhání potvrzení nebo vrácení čekajících transakcí může výrazně poškodit výkon, protože výchozí časový limit je jedna minuta TransactionScope . Pokud nepoužíváte příkaz, musíte provést veškerou using Try práci v bloku a explicitně volat Dispose metodu Finally v bloku.

Pokud dojde k výjimce v TransactionScope, transakce je označena jako nekonzistentní a je opuštěna. Po vyřazení se vrátí zpět TransactionScope . Pokud nedojde k žádné výjimce, zúčastněné transakce potvrzení.

Poznámka:

Třída TransactionScope vytvoří transakci s výchozí hodnotou IsolationLevel Serializable . V závislosti na vaší aplikaci můžete zvážit snížení úrovně izolace, abyste se vyhnuli vysokým kolizím v aplikaci.

Poznámka:

Doporučujeme provádět pouze aktualizace, vkládání a odstraňování v distribuovaných transakcích, protože spotřebovávají významné databázové prostředky. Příkazy Select mohou zbytečně uzamknout databázové prostředky a v některých scénářích možná budete muset pro výběry použít transakce. Jakákoli jiná než databázová práce by měla být provedena mimo rozsah transakce, pokud nezahrnuje jiné správce prostředků, kteří provádějí transakce. I když výjimka v oboru transakce brání transakce v potvrzení, TransactionScope třída nemá žádné zřízení pro vrácení zpět všechny změny, které váš kód provedl mimo rozsah samotné transakce. Pokud musíte provést nějakou akci při vrácení transakce zpět, musíte napsat vlastní implementaci IEnlistmentNotification rozhraní a explicitně zařazení do transakce.

Příklad

Práce se vyžaduje System.Transactions , abyste měli odkaz na System.Transactions.dll.

Následující funkce ukazuje, jak vytvořit promotable transakce proti dvěma různým instancím SQL Serveru reprezentované dvěma různými SqlConnection objekty, které jsou zabaleny do TransactionScope bloku. Kód vytvoří TransactionScope blok příkazem using a otevře první připojení, které ho automaticky zařadí do TransactionScopesouboru . Transakce je původně zařazena jako jednoduchá transakce, nikoli úplná distribuovaná transakce. Druhé připojení je zapsáno pouze v TransactionScope případě, že příkaz v prvním připojení nevyvolá výjimku. Při otevření druhého připojení se transakce automaticky zvýší na úplnou distribuovanou transakci. Metoda Complete je vyvolána, která potvrdí transakci pouze v případě, že nebyly vyvolány žádné výjimky. Pokud byla v jakémkoli okamžiku TransactionScope v bloku vyvolána výjimka, Complete nebude volána a distribuovaná transakce se vrátí zpět, když TransactionScope je uvolněn na konci jeho using bloku.

// This function takes arguments for the 2 connection strings and commands in order
// to create a transaction involving two SQL Servers. It returns a value > 0 if the
// transaction committed, 0 if the transaction rolled back. To test this code, you can
// connect to two different databases on the same server by altering the connection string,
// or to another RDBMS such as Oracle by altering the code in the connection2 code block.
static public int CreateTransactionScope(
    string connectString1, string connectString2,
    string commandText1, string commandText2)
{
    // Initialize the return value to zero and create a StringWriter to display results.
    int returnValue = 0;
    System.IO.StringWriter writer = new System.IO.StringWriter();

    // Create the TransactionScope in which to execute the commands, guaranteeing
    // that both commands will commit or roll back as a single unit of work.
    using (TransactionScope scope = new TransactionScope())
    {
        using (SqlConnection connection1 = new SqlConnection(connectString1))
        {
            try
            {
                // Opening the connection automatically enlists it in the
                // TransactionScope as a lightweight transaction.
                connection1.Open();

                // Create the SqlCommand object and execute the first command.
                SqlCommand command1 = new SqlCommand(commandText1, connection1);
                returnValue = command1.ExecuteNonQuery();
                writer.WriteLine("Rows to be affected by command1: {0}", returnValue);

                // if you get here, this means that command1 succeeded. By nesting
                // the using block for connection2 inside that of connection1, you
                // conserve server and network resources by opening connection2
                // only when there is a chance that the transaction can commit.
                using (SqlConnection connection2 = new SqlConnection(connectString2))
                    try
                    {
                        // The transaction is promoted to a full distributed
                        // transaction when connection2 is opened.
                        connection2.Open();

                        // Execute the second command in the second database.
                        returnValue = 0;
                        SqlCommand command2 = new SqlCommand(commandText2, connection2);
                        returnValue = command2.ExecuteNonQuery();
                        writer.WriteLine("Rows to be affected by command2: {0}", returnValue);
                    }
                    catch (Exception ex)
                    {
                        // Display information that command2 failed.
                        writer.WriteLine("returnValue for command2: {0}", returnValue);
                        writer.WriteLine("Exception Message2: {0}", ex.Message);
                    }
            }
            catch (Exception ex)
            {
                // Display information that command1 failed.
                writer.WriteLine("returnValue for command1: {0}", returnValue);
                writer.WriteLine("Exception Message1: {0}", ex.Message);
            }
        }

        // If an exception has been thrown, Complete will not
        // be called and the transaction is rolled back.
        scope.Complete();
    }

    // The returnValue is greater than 0 if the transaction committed.
    if (returnValue > 0)
    {
        writer.WriteLine("Transaction was committed.");
    }
    else
    {
        // You could write additional business logic here, notify the caller by
        // throwing a TransactionAbortedException, or log the failure.
        writer.WriteLine("Transaction rolled back.");
    }

    // Display messages.
    Console.WriteLine(writer.ToString());

    return returnValue;
}
' This function takes arguments for the 2 connection strings and commands in order
' to create a transaction involving two SQL Servers. It returns a value > 0 if the
' transaction committed, 0 if the transaction rolled back. To test this code, you can
' connect to two different databases on the same server by altering the connection string,
' or to another RDBMS such as Oracle by altering the code in the connection2 code block.
Public Function CreateTransactionScope( _
  ByVal connectString1 As String, ByVal connectString2 As String, _
  ByVal commandText1 As String, ByVal commandText2 As String) As Integer

    ' Initialize the return value to zero and create a StringWriter to display results.
    Dim returnValue As Integer = 0
    Dim writer As System.IO.StringWriter = New System.IO.StringWriter

    ' Create the TransactionScope in which to execute the commands, guaranteeing
    ' that both commands will commit or roll back as a single unit of work.
    Using scope As New TransactionScope()
        Using connection1 As New SqlConnection(connectString1)
            Try
                ' Opening the connection automatically enlists it in the
                ' TransactionScope as a lightweight transaction.
                connection1.Open()

                ' Create the SqlCommand object and execute the first command.
                Dim command1 As SqlCommand = New SqlCommand(commandText1, connection1)
                returnValue = command1.ExecuteNonQuery()
                writer.WriteLine("Rows to be affected by command1: {0}", returnValue)

                ' If you get here, this means that command1 succeeded. By nesting
                ' the Using block for connection2 inside that of connection1, you
                ' conserve server and network resources by opening connection2
                ' only when there is a chance that the transaction can commit.
                Using connection2 As New SqlConnection(connectString2)
                    Try
                        ' The transaction is promoted to a full distributed
                        ' transaction when connection2 is opened.
                        connection2.Open()

                        ' Execute the second command in the second database.
                        returnValue = 0
                        Dim command2 As SqlCommand = New SqlCommand(commandText2, connection2)
                        returnValue = command2.ExecuteNonQuery()
                        writer.WriteLine("Rows to be affected by command2: {0}", returnValue)

                    Catch ex As Exception
                        ' Display information that command2 failed.
                        writer.WriteLine("returnValue for command2: {0}", returnValue)
                        writer.WriteLine("Exception Message2: {0}", ex.Message)
                    End Try
                End Using

            Catch ex As Exception
                ' Display information that command1 failed.
                writer.WriteLine("returnValue for command1: {0}", returnValue)
                writer.WriteLine("Exception Message1: {0}", ex.Message)
            End Try
        End Using

        ' If an exception has been thrown, Complete will
        ' not be called and the transaction is rolled back.
        scope.Complete()
    End Using

    ' The returnValue is greater than 0 if the transaction committed.
    If returnValue > 0 Then
        writer.WriteLine("Transaction was committed.")
    Else
        ' You could write additional business logic here, notify the caller by
        ' throwing a TransactionAbortedException, or log the failure.
       writer.WriteLine("Transaction rolled back.")
     End If

    ' Display messages.
    Console.WriteLine(writer.ToString())

    Return returnValue
End Function

Viz také