Freigeben über


System.Transactions-Integration in SQL Server

Gilt für: .NET Framework .NET .NET Standard

Herunterladen von ADO.NET

.NET enthält ein neues Transaktionsframework, auf das über den System.Transactions-Namespace zugegriffen werden kann. Dieses Framework stellt Transaktionen vollständig in .NET, einschließlich ADO.NET, integriert bereit.

Neben den Verbesserungen in Bezug auf die Programmierbarkeit können System.Transactions und ADO.NET auch zusammenarbeiten, um Optimierungen bei der Arbeit mit Transaktionen zu koordinieren. Eine heraufstufbare Transaktion ist eine kompakte (lokale) Transaktion, die automatisch bei Bedarf auf eine vollverteilte Transaktion höhergestuft werden kann.

Der Microsoft SqlClient-Datenanbieter für SQL Server unterstützt höherstufbare Transaktionen, wenn Sie mit SQL Server arbeiten. Eine heraufstufbare Transaktion ruft den zusätzlichen Aufwand einer verteilten Transaktion nur hervor, wenn dieser erforderlich ist. Höherstufbare Transaktionen sind automatisch und erfordern keinen Eingriff des Entwicklers.

Erstellen von höherstufbaren Transaktionen

Der Microsoft SqlClient-Datenanbieter für SQL Server bietet Unterstützung für höherstufbare Transaktionen, die über die Klassen im System.Transactions-Namespace verarbeitet werden. Heraufstufbare Transaktionen optimieren verteilte Transaktionen, indem sie das Erstellen einer verteilten Transaktion verzögern, bis diese benötigt wird. Wenn nur ein Ressourcen-Manager erforderlich ist, erfolgt keine verteilte Transaktion.

Hinweis

In einem teilweise vertrauenswürdigen Szenario wird die DistributedTransactionPermission benötigt, wenn eine Transaktion zu einer verteilten Transaktion heraufgestuft wird.

Szenarios mit höherstufbaren Transaktionen

Heraufstufbare Transaktionen beanspruchen normalerweise erhebliche Systemressourcen und werden von MS DTC (Microsoft Distributed Transaction Coordinator) verwaltet, der alle Ressourcen-Manager integriert, auf die in der Transaktion zugegriffen wird. Eine höherstufbare Transaktion ist eine spezielle Art von System.Transactions-Transaktion, die im Prinzip die Arbeit an eine einfache SQL Server-Transaktion delegiert. System.Transactions, Microsoft.Data.SqlClient und SQL Server koordinieren die für die Verarbeitung der Transaktion erforderliche Arbeit und stufen sie bei Bedarf auf eine vollständig verteilte Transaktion hoch.

Der Vorteil der Verwendung heraufstufbarer Transaktionen besteht darin, dass beim Öffnen einer Verbindung mit einer aktiven TransactionScope -Transaktion die Transaktion als kompakte Transaktion durchgeführt wird, wenn keine weiteren Verbindungen geöffnet sind und der zusätzliche Mehraufwand einer vollständig verteilten Transaktion vermieden werden kann.

Schlüsselwörter für Verbindungszeichenfolgen

Die ConnectionString -Eigenschaft unterstützt ein Schlüsselwort, Enlist, das angibt, ob der Microsoft.Data.SqlClient Transaktionskontexte erkennt, und die Verbindung automatisch in einer verteilten Transaktion einträgt. Wenn Enlist=true, wird die Verbindung automatisch im aktuellen Transaktionskontext des öffnenden Threads eingetragen. Wenn Enlist=false, interagiert der SqlClient nicht mit einer verteilten Transaktion. Der Standardwert für Enlist ist "true". Wenn Enlist in der Verbindungszeichenfolge nicht angegeben ist, wird die Verbindung automatisch in einer verteilten Transaktion eingetragen, wenn eine solche zum Zeitpunkt des Öffnens der Verbindung erkannt wird.

Die Transaction Binding -Schlüsselwörter in einer SqlConnection -Verbindungszeichenfolge steuern die Zuordnung einer Verbindung mit einer eingetragenen System.Transactions -Transaktion. Diese ist auch verfügbar durch die TransactionBinding -Eigenschaft eines SqlConnectionStringBuilder.

In der folgenden Tabelle sind die möglichen Werte beschrieben.

Stichwort BESCHREIBUNG
Implicit Unbind Der Standardwert. Die Verbindung wird von der Transaktion getrennt, wenn diese endet, und wechselt damit zurück in den Autocommit-Modus.
Explicit Unbind Die Verbindung bleibt so lange an die Transaktion gebunden, bis die Transaktion geschlossen wird. Die Verbindung schlägt fehl, wenn die zugehörige Transaktion nicht aktiv ist oder Currentnicht entspricht.

Verwenden von "TransactionScope"

Die TransactionScope-Klasse bewirkt, dass ein Codeblock transaktional wird, indem sie Verbindungen implizit in einer verteilten Transaktion einträgt. Sie müssen die Complete -Methode am Ende des TransactionScope -Blocks vor dem Verlassen aufrufen. Beim Verlassen des Blocks wird die Dispose -Methode aufgerufen. Wenn eine Ausnahme aufgerufen wurde, die den Code veranlasst, den Bereich zu verlassen, wird die Transaktion als abgebrochen angesehen.

Es wird empfohlen, einen using -Block zu verwenden, um sicherzustellen, dass Dispose für das TransactionScope -Objekt aufgerufen wird, wenn der verwendete Block beendet wird. Durch das Fehlschlagen von Commits oder Rollbacks ausstehender Transaktionen kann die Leistung entscheidend beeinträchtigt werden, da das Zeitlimit für TransactionScope eine Minute beträgt. Wenn Sie keine using-Anweisung verwenden, müssen Sie alle Arbeiten in einem Try-Block ausführen und die Dispose-Methode im Finally-Block explizit aufrufen.

Wenn innerhalb des TransactionScopeeine Ausnahme auftritt, wird die Transaktion als inkonsistent markiert und abgebrochen. Sie wird zurückgesetzt, wenn der TransactionScope verworfen wird. Wenn keine Ausnahme auftritt, wird für teilnehmende Transaktionen ein Commit ausgeführt.

Hinweis

Die TransactionScope-Klasse erstellt IsolationLevel standardmäßig mit dem Wert Serializable. Je nach Ihrer Anwendung kann es vorteilhaft sein, die Isolationsstufe herabzusetzen, um ein hohes Konfliktpotenzial in der Anwendung zu vermeiden.

Hinweis

Es wird empfohlen, dass Sie nur Update-, Einfüge- und Löschvorgänge in verteilten Transaktionen durchführen, da diese erhebliche Datenbankressorcen beanspruchen. Select-Anweisungen können Datenbankressourcen unnötigerweise blockieren, und in einigen Szenarien kann es erforderlich sein, Transaktionen für Select-Vorgänge zu verwenden. Arbeiten, die nicht mit der Datenbank zusammenhängen, sollten außerhalb des Bereichs der Transaktion durchgeführt werden, außer wenn andere transaktive Ressourcen-Manager verwendet werden. Obwohl eine Ausnahme innerhalb des Bereichs der Transaktion dazu führt, dass die Transaktion keinen Commit ausführt, verfügt die TransactionScope -Klasse über keine Funktion zum Zurücksetzen von Änderungen, die vom Code außerhalb des Bereichs der Transaktion selbst durchgeführt wurden. Wenn Sie beim Zurücksetzen der Transaktion Maßnahmen ergreifen müssen, müssen Sie Ihre eigene Implementierung der IEnlistmentNotification -Schnittstelle schreiben und in der Transaktion explizit eintragen.

Beispiel

Das Arbeiten mit System.Transactions setzt einen Verweis auf "System.Transactions.dll" voraus.

Die folgende Funktion zeigt das Erstellen einer heraufstufbaren Transaktion für zwei verschiedene SQL Server-Instanzen, die von zwei verschiedenen SqlConnection -Objekten dargestellt werden, die ihrerseits von einem TransactionScope -Block umschlossen sind.

Mit dem Code unten wird der TransactionScope-Block mit einer using-Anweisung erstellt und die erste Verbindung geöffnet, wodurch die Transaktion automatisch in TransactionScope eingetragen wird.

Die Transaktion wird anfänglich als einfache Transaktion, nicht als vollständig verteilte Transaktion, eingetragen. Die zweite Verbindung wird nur dann im TransactionScope eingetragen, wenn der Befehl in der ersten Verbindung keine Ausnahme auslöst. Wenn die zweite Verbindung geöffnet wird, wird die Transaktion automatisch auf eine vollständig verteilte Transaktion hochgestuft.

Später wird die Complete-Methode aufgerufen, die die Transaktion nur dann committet, wenn keine Ausnahmen ausgelöst wurden. Wenn an einem beliebigen Punkt im TransactionScope -Block eine Ausnahme ausgelöst wurde, wird Complete nicht aufgerufen, und die verteilte Transaktion wird zurückgenommen, sobald der TransactionScope am Ende seines using -Blocks verfügbar gemacht wird.

using System;
using System.Transactions;
using Microsoft.Data.SqlClient;

class Program
{
    static void Main(string[] args)
    {
        string connectionString = "Data Source = localhost; Integrated Security = true; Initial Catalog = AdventureWorks";

        string commandText1 = "INSERT INTO Production.ScrapReason(Name) VALUES('Wrong size')";
        string commandText2 = "INSERT INTO Production.ScrapReason(Name) VALUES('Wrong color')";

        int result = CreateTransactionScope(connectionString, connectionString, commandText1, commandText2);

        Console.WriteLine("result = " + result);
    }

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

Weitere Informationen: