Delen via


System.Transactions-integratie met SQL Server

Van toepassing op: .NET Framework .NET Standard

ADO.NET downloaden

.NET bevat een transactieframework dat toegankelijk is via de System.Transactions naamruimte. Dit framework maakt transacties beschikbaar op een manier die volledig is geïntegreerd in .NET, met inbegrip van ADO.NET.

Naast de verbeteringen in de programmeerbaarheid kunnen System.Transactions en ADO.NET samenwerken om optimalisaties af te stemmen wanneer u met transacties werkt. Een promotabele transactie is een lichtgewicht (lokale) transactie die automatisch kan worden gepromoveerd naar een volledig gedistribueerde transactie op basis van behoefte.

De Microsoft SqlClient-gegevensprovider voor SQL Server ondersteunt promotietransacties wanneer u met SQL Server werkt. Een promotiebare transactie roept de toegevoegde overhead van een gedistribueerde transactie niet aan, tenzij de toegevoegde overhead vereist is. Promotable transacties zijn automatisch en vereisen geen tussenkomst van de ontwikkelaar.

Promotietransacties maken

De Microsoft SqlClient-gegevensprovider voor SQL Server biedt ondersteuning voor promotietransacties die worden verwerkt via de klassen in de System.Transactions naamruimte. Promotable-transacties optimaliseren gedistribueerde transacties door het maken van een gedistribueerde transactie uit te stellen totdat deze nodig is. Als er slechts één Resource Manager vereist is, vindt er geen gedistribueerde transactie plaats.

Opmerking

In een gedeeltelijk vertrouwd scenario is dit DistributedTransactionPermission vereist wanneer een transactie wordt gepromoveerd naar een gedistribueerde transactie.

Promotiescenario's voor transacties

Gedistribueerde transacties verbruiken doorgaans aanzienlijke systeemresources, die worden beheerd door Microsoft Distributed Transaction Coordinator (MS DTC), waarmee alle resourcemanagers die in de transactie zijn geopend, worden geïntegreerd. Een promotable transactie is een speciale vorm van een System.Transactions transactie die het werk effectief delegeert aan een eenvoudige SQL Server-transactie. System.Transactions, Microsoft.Data.SqlClienten SQL Server coördineren het werk dat betrokken is bij het afhandelen van de transactie, waarbij het naar behoefte wordt gepromoot naar een volledige gedistribueerde transactie.

Het voordeel van het gebruik van promotable transacties is dat wanneer een verbinding wordt geopend met behulp van een actieve TransactionScope transactie en er geen andere verbindingen worden geopend, de transactie wordt uitgevoerd als een lichtgewicht transactie, waardoor de extra overhead van een volledige gedistribueerde transactie wordt vermeden.

Trefwoorden voor verbindingsreeks

De ConnectionString eigenschap ondersteunt een trefwoord, Enlistwaarmee wordt aangegeven of Microsoft.Data.SqlClient transactionele contexten worden gedetecteerd en de verbinding automatisch wordt opgenomen in een gedistribueerde transactie. Als Enlist=true wordt de verbinding automatisch opgenomen in de huidige transactiecontext van de thread die geopend wordt. Als Enlist=false, communiceert de SqlClient verbinding niet met een gedistribueerde transactie. De standaardwaarde voor Enlist is true. Als Enlist niet is opgegeven in de verbindingsreeks, wordt de verbinding automatisch ingeschreven in een gedistribueerde transactie als een transactie wordt gedetecteerd wanneer de verbinding wordt geopend.

De Transaction Binding trefwoorden in een SqlConnection verbindingsreeks bepalen de koppeling van de verbinding met een in de lijst opgenomen System.Transactions transactie. Het is ook beschikbaar via de TransactionBinding eigenschap van een SqlConnectionStringBuilder.

In de volgende tabel worden de mogelijke waarden beschreven.

Keyword Description
Impliciete ontkoppeling De standaardwaarde. De verbinding wordt losgekoppeld van de transactie wanneer deze eindigt en schakelt terug naar de autocommit-modus.
Expliciete koppeling ongedaan maken De verbinding blijft gekoppeld aan de transactie totdat de transactie is gesloten. De verbinding mislukt als de gekoppelde transactie niet actief is of niet overeenkomt Current.

TransactionScope gebruiken

De TransactionScope klasse maakt een codeblok transactioneel door impliciet verbindingen in te schakelen in een gedistribueerde transactie. U moet de Complete methode aan het einde van het TransactionScope blok aanroepen voordat u het verlaat. Als u het blok verlaat, wordt de Dispose methode aangeroepen. Als er een uitzondering is gegenereerd waardoor de code het bereik verlaat, wordt de transactie beschouwd als afgebroken.

Wij raden aan een using blok te gebruiken om ervoor te zorgen dat Dispose op het TransactionScope object wordt aangeroepen wanneer het using-blok wordt afgesloten. Fouten bij het doorvoeren of terugdraaien van transacties in behandeling kunnen de prestaties aanzienlijk beschadigen, omdat de standaardtime-out voor de transactie TransactionScope één minuut is. Als u geen using instructie gebruikt, moet u alle werkzaamheden in een Try blok uitvoeren en de Dispose methode in het Finally blok expliciet aanroepen.

Als er een uitzondering optreedt in de TransactionScopetransactie, wordt de transactie gemarkeerd als inconsistent en wordt deze afgelaten. Het wordt teruggedraaid wanneer het TransactionScope wordt verwijderd. Als er geen uitzondering optreedt, committeren de deelnemende transacties.

Opmerking

De TransactionScope klasse creëert standaard een transactie met een IsolationLevel van Serializable. Afhankelijk van uw toepassing kunt u overwegen het isolatieniveau te verlagen om conflicten in uw toepassing te voorkomen.

Opmerking

U wordt aangeraden alleen updates, invoegingen en verwijderingen uit te voeren binnen gedistribueerde transacties, omdat ze aanzienlijke databasebronnen verbruiken. Select-instructies kunnen databasebronnen onnodig vergrendelen. In sommige scenario's moet u mogelijk transacties gebruiken voor selecties. Alle niet-databasewerkzaamheden moeten buiten het bereik van de transactie worden uitgevoerd, tenzij er andere transactiegebaseerde resourcemanagers betrokken zijn. Hoewel een uitzondering in het bereik van de transactie voorkomt dat de transactie wordt doorgevoerd, heeft de TransactionScope klasse geen inrichting voor het terugdraaien van wijzigingen die uw code heeft aangebracht buiten het bereik van de transactie zelf. Als u actie moet ondernemen wanneer de transactie wordt teruggedraaid, moet u uw eigen implementatie van de IEnlistmentNotification interface schrijven en expliciet in de transactie opnemen.

Example

Werken met System.Transactions vereist dat u een verwijzing naar System.Transactions.dllhebt.

De volgende functie laat zien hoe u een promotiebare transactie maakt op twee verschillende SQL Server-exemplaren, vertegenwoordigd door twee verschillende SqlConnection objecten, die in een TransactionScope blok zijn verpakt.

Met de onderstaande code wordt het TransactionScope blok gemaakt met een using instructie en wordt de eerste verbinding geopend, die automatisch in de TransactionScope wordt geregistreerd.

De transactie wordt in eerste instantie als een lichtgewicht transactie ingeschreven, niet als een volledige gedistribueerde transactie. De tweede verbinding wordt in de TransactionScope alleen opgenomen als de opdracht in de eerste verbinding geen uitzondering genereert. Wanneer de tweede verbinding wordt geopend, wordt de transactie automatisch gepromoveerd naar een volledige gedistribueerde transactie.

Later wordt de Complete methode aangeroepen, die de transactie alleen doorvoert als er geen uitzonderingen zijn opgetreden. Als er op enig moment in het TransactionScope blok een uitzondering is opgetreden, wordt Complete niet aangeroepen en wordt de transactie teruggedraaid wanneer de gedistribueerde transactie aan het einde van zijn TransactionScope blok wordt verwijderd.

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

Zie ook