Dela via


System.Transactions-integrering med SQL Server

.NET Framework version 2.0 introducerade ett transaktionsramverk som kan nås via System.Transactions namnområdet. Det här ramverket exponerar transaktioner på ett sätt som är helt integrerat i .NET Framework, inklusive ADO.NET.

Förutom programmability-förbättringarna System.Transactions kan ADO.NET arbeta tillsammans för att samordna optimeringar när du arbetar med transaktioner. En promotable-transaktion är en enkel (lokal) transaktion som automatiskt kan befordras till en fullständigt distribuerad transaktion efter behov.

Från och med ADO.NET 2.0 har System.Data.SqlClient stöd för kampanjtabelltransaktioner när du arbetar med SQL Server. En promotable-transaktion anropar inte den extra kostnaden för en distribuerad transaktion såvida inte den extra kostnaden krävs. Promotable-transaktioner är automatiska och kräver inga åtgärder från utvecklaren.

Promotable-transaktioner är endast tillgängliga när du använder .NET Framework Data Provider för SQL Server (SqlClient) med SQL Server.

Skapa kampanjtabelltransaktioner

.NET Framework-providern för SQL Server har stöd för promotable-transaktioner som hanteras via klasserna i .NET Framework-namnområdet System.Transactions . Promotable-transaktioner optimerar distribuerade transaktioner genom att skjuta upp skapandet av en distribuerad transaktion tills den behövs. Om endast en resurshanterare krävs sker ingen distribuerad transaktion.

Kommentar

I ett delvis betrott scenario DistributedTransactionPermission krävs när en transaktion befordras till en distribuerad transaktion.

Transaktionsscenarier för promotable

Distribuerade transaktioner förbrukar vanligtvis betydande systemresurser som hanteras av Microsoft Distributed Transaction Coordinator (MS DTC), som integrerar alla resurshanterare som används i transaktionen. En promotable-transaktion är en särskild form av en System.Transactions transaktion som effektivt delegerar arbetet till en enkel SQL Server-transaktion. System.Transactions, System.Data.SqlClient, och SQL Server samordnar arbetet med att hantera transaktionen och befordrar den till en fullständig distribuerad transaktion efter behov.

Fördelen med att använda promotable-transaktioner är att när en anslutning öppnas med hjälp av en aktiv TransactionScope transaktion, och inga andra anslutningar öppnas, checkar transaktionen in som en enkel transaktion, i stället för att medföra ytterligare omkostnader för en fullständig distribuerad transaktion.

nyckelord för Anslut ionsträng

Egenskapen ConnectionString stöder ett nyckelord, Enlist, som anger om System.Data.SqlClient transaktionskontexter ska identifieras och anslutningen automatiskt registreras i en distribuerad transaktion. Om Enlist=trueregistreras anslutningen automatiskt i öppningstrådens aktuella transaktionskontext. SqlClient Om Enlist=falseinteragerar inte anslutningen med en distribuerad transaktion. Standardvärdet för Enlist är sant. Om Enlist inte anges i anslutningssträng registreras anslutningen automatiskt i en distribuerad transaktion om en identifieras när anslutningen öppnas.

Nyckelorden Transaction Binding i en SqlConnection anslutningssträng styra anslutningens association med en listad System.Transactions transaktion. Den är också tillgänglig via TransactionBinding egenskapen för en SqlConnectionStringBuilder.

I följande tabell beskrivs möjliga värden.

Nyckelord beskrivning
Implicit avbindning Standardvärdet. Anslutningen kopplar från transaktionen när den avslutas och växlar tillbaka till automatiskt återtagandeläge.
Explicit avbindning Anslutningen förblir kopplad till transaktionen tills transaktionen har stängts. Anslutningen misslyckas om den associerade transaktionen inte är aktiv eller inte matchar Current.

Använda TransactionScope

Klassen TransactionScope gör ett kodblock transaktionellt genom att implicit registrera anslutningar i en distribuerad transaktion. Du måste anropa Complete metoden i slutet av TransactionScope blocket innan du lämnar den. Om du lämnar blocket anropas Dispose metoden. Om ett undantag har genererats som gör att koden lämnar omfånget anses transaktionen vara avbruten.

Vi rekommenderar att du använder ett using block för att se till att Dispose det anropas på TransactionScope objektet när användningsblocket avslutas. Om det inte går att checka in eller återställa väntande transaktioner kan prestandan avsevärt skadas eftersom tidsgränsen TransactionScope för standardvärdet är en minut. Om du inte använder en using -instruktion måste du utföra allt arbete i ett Try block och uttryckligen anropa Dispose metoden i Finally blocket.

Om ett undantag inträffar i TransactionScopemarkeras transaktionen som inkonsekvent och avbryts. Den återställs när den TransactionScope tas bort. Om inget undantag inträffar genomför deltagande transaktioner.

Kommentar

Klassen TransactionScope skapar en transaktion med en IsolationLevel av Serializable som standard. Beroende på ditt program kanske du vill överväga att sänka isoleringsnivån för att undvika hög konkurrens i ditt program.

Kommentar

Vi rekommenderar att du endast utför uppdateringar, infogningar och borttagningar i distribuerade transaktioner eftersom de förbrukar betydande databasresurser. Select-instruktioner kan låsa databasresurser i onödan, och i vissa scenarier kan du behöva använda transaktioner för val. Allt arbete som inte är databasarbete bör utföras utanför transaktionens omfång, såvida det inte omfattar andra transacted-resurshanterare. Även om ett undantag i transaktionens omfång hindrar transaktionen från att genomföras, TransactionScope har klassen ingen möjlighet att återställa eventuella ändringar som koden har gjort utanför själva transaktionens omfång. Om du måste vidta åtgärder när transaktionen återställs måste du skriva en egen implementering av IEnlistmentNotification gränssnittet och uttryckligen registrera dig i transaktionen.

Exempel

Att arbeta med System.Transactions kräver att du har en referens till System.Transactions.dll.

Följande funktion visar hur du skapar en promotable-transaktion mot två olika SQL Server-instanser, som representeras av två olika SqlConnection objekt, som omsluts i ett TransactionScope block. Koden skapar TransactionScope blocket med en using -instruktion och öppnar den första anslutningen, som automatiskt registrerar den i TransactionScope. Transaktionen registreras ursprungligen som en enkel transaktion, inte som en fullständig distribuerad transaktion. Den andra anslutningen registreras endast TransactionScope om kommandot i den första anslutningen inte utlöser ett undantag. När den andra anslutningen öppnas höjs transaktionen automatiskt till en fullständig distribuerad transaktion. Metoden Complete anropas, vilket endast checkar in transaktionen om inga undantag har genererats. Om ett undantag har genererats någon gång i TransactionScope blocket Complete anropas inte och den distribuerade transaktionen återställs när den TransactionScope tas bort i slutet av blocket using .

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

Se även