Integrazione di System.Transactions con SQL ServerSystem.Transactions Integration with SQL Server

Nel .NET Framework versione 2,0 è stato introdotto un Framework di transazione a cui è System.Transactions possibile accedere tramite lo spazio dei nomi.The .NET Framework version 2.0 introduced a transaction framework that can be accessed through the System.Transactions namespace. Questo framework espone le transazioni in modo completamente integrato nel .NET Framework, incluso ADO.NET.This framework exposes transactions in a way that is fully integrated in the .NET Framework, including ADO.NET.

Oltre ai miglioramenti apportati alla programmabilità System.Transactions , e ADO.NET possono interagire per coordinare le ottimizzazioni quando si utilizzano le transazioni.In addition to the programmability enhancements, System.Transactions and ADO.NET can work together to coordinate optimizations when you work with transactions. Una transazione promuovibile è una transazione di tipo semplice (locale) che può essere promossa in modo automatico a una transazione completamente distribuita in base alle esigenze.A promotable transaction is a lightweight (local) transaction that can be automatically promoted to a fully distributed transaction on an as-needed basis.

A partire da ADO.NET 2,0 System.Data.SqlClient , supporta le transazioni promuovibili quando si lavora con SQL Server.Starting with ADO.NET 2.0, System.Data.SqlClient supports promotable transactions when you work with SQL Server. Una transazione promuovibile non richiama l'overhead aggiunto di una transazione distribuita, a meno che non sia necessario.A promotable transaction does not invoke the added overhead of a distributed transaction unless the added overhead is required. Le transazioni promuovibili sono automatiche e non richiedono alcun intervento da parte dello sviluppatore.Promotable transactions are automatic and require no intervention from the developer.

Le transazioni promuovibili sono disponibili solo quando si usa il .NET Framework provider di dati perSqlClientSQL Server () con SQL Server.Promotable transactions are only available when you use the .NET Framework Data Provider for SQL Server (SqlClient) with SQL Server.

Creazione di transazioni promuovibiliCreating Promotable Transactions

Il provider .NET Framework per SQL Server fornisce il supporto per le transazioni promuovibili, che vengono gestite tramite le classi System.Transactions nello spazio dei nomi .NET Framework.The .NET Framework Provider for SQL Server provides support for promotable transactions, which are handled through the classes in the .NET Framework System.Transactions namespace. Le transazioni promuovibili consentono di ottimizzare le transazioni distribuite rinviandone la creazione finché non è richiesta.Promotable transactions optimize distributed transactions by deferring creating a distributed transaction until it is needed. Se è necessario un solo gestore di risorse, non si verificherà alcuna transazione distribuita.If only one resource manager is required, no distributed transaction occurs.

Nota

In un scenario ad attendibilità parziale è richiesto DistributedTransactionPermission quando una transazione viene promossa a transazione distribuita.In a partially trusted scenario, the DistributedTransactionPermission is required when a transaction is promoted to a distributed transaction.

Scenari di transazioni promuovibiliPromotable Transaction Scenarios

In genere, le transazioni distribuite richiedono un notevole utilizzo delle risorse, in quanto sono gestite da Microsoft Distributed Transaction Coordinator (MS DTC) che integra tutti i gestori delle risorse a cui si accede nella transazione.Distributed transactions typically consume significant system resources, being managed by Microsoft Distributed Transaction Coordinator (MS DTC), which integrates all the resource managers accessed in the transaction. Una transazione promuovibile è una forma speciale di System.Transactions transazione che delega efficacemente il lavoro a una semplice transazione di SQL Server.A promotable transaction is a special form of a System.Transactions transaction that effectively delegates the work to a simple SQL Server transaction. System.Transactions, System.Data.SqlCliente SQL Server coordinano il lavoro necessario per gestire la transazione, promuovendo tale operazione a una transazione distribuita completa in base alle esigenze.System.Transactions, System.Data.SqlClient, and SQL Server coordinate the work involved in handling the transaction, promoting it to a full distributed transaction as needed.

Il vantaggio derivante dall'utilizzo delle transazioni promuovibili consiste nel fatto che quando si apre una connessione tramite un transazione TransactionScope attiva e non sono presenti altre transazioni aperte, viene eseguito il commit della transazione come tipo semplice, anziché provocare l'overhead aggiuntivo di una piena transazione distribuita.The benefit of using promotable transactions is that when a connection is opened by using an active TransactionScope transaction, and no other connections are opened, the transaction commits as a lightweight transaction, instead of incurring the additional overhead of a full distributed transaction.

Parole chiave della stringa di connessioneConnection String Keywords

La proprietà ConnectionString supporta la parola chiave Enlist, che indica se System.Data.SqlClient rileverà i contesti transazionali ed elenca automaticamente la connessione in una transazione distribuita.The ConnectionString property supports a keyword, Enlist, which indicates whether System.Data.SqlClient will detect transactional contexts and automatically enlist the connection in a distributed transaction. Se Enlist=true, la connessione verrà automaticamente elencata nel contesto di transazione corrente del thread di apertura.If Enlist=true, the connection is automatically enlisted in the opening thread's current transaction context. Se Enlist=false, la connessione SqlClient non interagirà con una transazione distribuita.If Enlist=false, the SqlClient connection does not interact with a distributed transaction. Il valore predefinito per Enlist è true.The default value for Enlist is true. Se Enlist non è specificato nella stringa di connessione e se viene rilevata una transazione distribuita all'apertura della connessione, quest'ultima verrà automaticamente elencata nella transazione distribuita.If Enlist is not specified in the connection string, the connection is automatically enlisted in a distributed transaction if one is detected when the connection is opened.

Le parole chiave Transaction Binding di una stringa di connessione SqlConnection controllano l'associazione della connessione a una transazione System.Transactions elencata.The Transaction Binding keywords in a SqlConnection connection string control the connection's association with an enlisted System.Transactions transaction. È anche disponibile tramite le proprietà TransactionBinding e SqlConnectionStringBuilder.It is also available through the TransactionBinding property of a SqlConnectionStringBuilder.

Nella tabella seguente sono descritti i valori possibili.The following table describes the possible values.

Parola chiaveKeyword DESCRIZIONEDescription
Implicit UnbindImplicit Unbind Valore predefinito.The default. La connessione viene scollegata dalla transazione una volta completata e torna alla modalità di commit automatico.The connection detaches from the transaction when it ends, switching back to autocommit mode.
Explicit UnbindExplicit Unbind La connessione rimane collegata alla transazione fino alla chiusura di quest'ultima.The connection remains attached to the transaction until the transaction is closed. La connessione non verrà eseguita se la transazione associata non è attiva o non corrisponde a Current.The connection will fail if the associated transaction is not active or does not match Current.

Uso di TransactionScopeUsing TransactionScope

La classe TransactionScope rende un blocco di codice transazionale elencando implicitamente le connessioni in una transazione distribuita.The TransactionScope class makes a code block transactional by implicitly enlisting connections in a distributed transaction. È necessario chiamare il metodo Complete alla fine del blocco TransactionScope prima di lasciarlo.You must call the Complete method at the end of the TransactionScope block before leaving it. Quando si lascia il blocco viene richiamato il metodo Dispose .Leaving the block invokes the Dispose method. Se è stata generata un'eccezione che ha provocato l'uscita del codice dall'ambito, la transazione sarà considerata interrotta.If an exception has been thrown that causes the code to leave scope, the transaction is considered aborted.

Si consiglia di usare un blocco using per assicurare che venga chiamato il metodo Dispose sull'oggetto TransactionScope quando si esce dal blocco using.We recommend that you use a using block to make sure that Dispose is called on the TransactionScope object when the using block is exited. Se non viene eseguito il commit o il rollback delle transazioni in sospeso, è possibile che le prestazioni vengano notevolmente compromesse, in quanto il timeout predefinito per TransactionScope è di un minuto.Failure to commit or roll back pending transactions can significantly damage performance because the default time-out for the TransactionScope is one minute. Se non si usa un'istruzione using , è necessario eseguire le operazioni in un blocco Try e chiamare in modo esplicito il metodo Dispose nel blocco Finally .If you do not use a using statement, you must perform all work in a Try block and explicitly call the Dispose method in the Finally block.

Se viene generata un'eccezione all'interno di TransactionScope, la transazione viene contrassegnata come incoerente e viene interrotta.If an exception occurs in the TransactionScope, the transaction is marked as inconsistent and is abandoned. Il rollback verrà eseguito quando viene eliminato il tipo TransactionScope .It will be rolled back when the TransactionScope is disposed. Se non viene generata alcuna eccezione, verrà eseguito il commit delle transazioni presenti.If no exception occurs, participating transactions commit.

Nota

Per impostazione predefinita, la classe TransactionScope crea una transazione in cui il valore di IsolationLevel è Serializable .The TransactionScope class creates a transaction with a IsolationLevel of Serializable by default. A seconda dell'applicazione in uso, potrebbe essere necessario ridurre il livello di isolamento per evitare un'elevata possibilità di conflitto nell'applicazione.Depending on your application, you might want to consider lowering the isolation level to avoid high contention in your application.

Nota

Si consiglia di eseguire solo operazioni di aggiornamento, inserimento ed eliminazione all'interno delle transazioni distribuite, poiché comportano un utilizzo notevole delle risorse di database.We recommend that you perform only updates, inserts, and deletes within distributed transactions because they consume significant database resources. Le istruzioni SELECT possono bloccare le risorse di database inutilmente e, in alcuni scenari, può essere necessario usare le transazioni per questo tipo di istruzioni.Select statements may lock database resources unnecessarily, and in some scenarios, you may have to use transactions for selects. È necessario eseguire le operazioni non di database al di fuori dell'ambito della transazione, a meno che non richiedano altri gestori di risorse transazionali.Any non-database work should be done outside the scope of the transaction, unless it involves other transacted resource managers. Sebbene un'eccezione nell'ambito della transazione impedisca l'esecuzione del commit, la classe TransactionScope non è in grado di eseguire il rollback delle modifiche apportate al codice al di fuori dell'ambito della transazione stessa.Although an exception in the scope of the transaction prevents the transaction from committing, the TransactionScope class has no provision for rolling back any changes your code has made outside the scope of the transaction itself. Per eseguire operazioni durante il rollback della transazione, è necessario scrivere la propria implementazione dell'interfaccia IEnlistmentNotification ed elencarla in modo esplicito nella transazione.If you have to take some action when the transaction is rolled back, you must write your own implementation of the IEnlistmentNotification interface and explicitly enlist in the transaction.

EsempioExample

Per usare System.Transactions , è necessario un riferimento a System.Transactions.dll.Working with System.Transactions requires that you have a reference to System.Transactions.dll.

Nella funzione seguente viene illustrato come creare una transazione promuovibile in due istanze diverse di SQL Server, rappresentate da due oggetti SqlConnection diversi, incapsulati in un blocco TransactionScope .The following function demonstrates how to create a promotable transaction against two different SQL Server instances, represented by two different SqlConnection objects, which are wrapped in a TransactionScope block. Il codice consente di creare il blocco TransactionScope con un'istruzione using , di aprire la prima connessione e di elencarla automaticamente nel tipo TransactionScope.The code creates the TransactionScope block with a using statement and opens the first connection, which automatically enlists it in the TransactionScope. Inizialmente la transazione viene elencata come transazione semplice e non come piena transazione distribuita.The transaction is initially enlisted as a lightweight transaction, not a full distributed transaction. La seconda connessione viene inserita in TransactionScope solo se il comando della prima connessione non genera un'eccezione.The second connection is enlisted in the TransactionScope only if the command in the first connection does not throw an exception. Quando viene aperta la seconda connessione, la transazione viene automaticamente promossa diventando una piena transazione distribuita.When the second connection is opened, the transaction is automatically promoted to a full distributed transaction. Viene richiamato il metodo Complete che esegue il commit della transazione solo se non sono state generate eccezioni.The Complete method is invoked, which commits the transaction only if no exceptions have been thrown. Se è stata generata un'eccezione in qualsiasi punto del blocco TransactionScope , il metodo Complete non verrà chiamato e verrà eseguito il rollback della transazione distribuita quando TransactionScope viene eliminato al termine del relativo blocco using .If an exception has been thrown at any point in the TransactionScope block, Complete will not be called, and the distributed transaction will roll back when the TransactionScope is disposed at the end of its using block.

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

Vedere ancheSee also