Integrazione di risorse come partecipanti a una transazione

[Questo argomento è una versione preliminare della documentazione e può essere soggetto a modifiche nelle versioni future. Sono presenti anche argomenti vuoti, utilizzati come segnaposto].

Ogni risorsa che partecipa a una transazione viene gestita da un gestore di risorse, le cui azioni vengono coordinate da una gestione transazioni. La coordinazione si basa sull'invio di notifiche ai partecipanti che hanno utilizzato la gestione transazioni per integrarsi nella transazione.

Questo argomento descrive come integrare una o più risorse in una transazione, nonché i vari tipi di integrazione. L'argomento Commit di una transazione monofase e multifase descrive invece come coordinare il commit di una transazione fra le risorse integrate.

Integrazione di risorse in una transazione

Per poter partecipare a una determinata transazione, una risorsa deve integrarsi in essa. La classe Transaction definisce un set di metodi i cui nomi iniziano con Enlist che forniscono questa funzionalità. I diversi metodi Enlist corrispondono ai vari tipi di integrazione disponibili in un gestore di risorse. In particolare, i metodi EnlistVolatile vengono utilizzati per le risorse volatili, mentre i metodi EnlistDurable vengono utilizzati per le risorse durevoli. La durabilità (o la volatilità) di un gestore di risorse ne indica la capacità (o la non capacità) di ripristino in caso di errore. I gestori di risorse che supportano il ripristino in caso di errore salvano i dati in modo permanente in un archivio durevole durante la fase 1, ovvero la fase di preparazione. In questo modo, se il gestore di risorse diventa non disponibile, può reintegrarsi nella transazione non appena viene ripristinato e quindi eseguire le azioni appropriate in base alle notifiche ricevute dalla gestione transazioni. In generale, i gestori di risorse volatili gestiscono risorse volatili, come nel caso di una struttura di dati in memoria (ad esempio, una tabella hash transazionale in memoria), laddove i gestori di risorse durevoli gestiscono risorse che presentano un archivio di backup permanente, come nel caso di un database con archivio di backup su disco.

Per semplicità, dopo aver deciso se utilizzare il metodo EnlistDurable o il metodo EnlistVolatile a seconda del supporto di durabilità della risorsa utilizzata, è necessario integrare la risorsa nel protocollo 2PC (2-Phase Commit, commit a due fasi) implementando nel gestore di risorse l'interfaccia IEnlistmentNotification. Per ulteriori informazioni sul protocollo 2PC, vedere Commit di una transazione monofase e multifase.

Un determinato partecipante può integrarsi in uno o più di questi protocolli chiamando più volte i metodi EnlistDurable e EnlistVolatile.

Integrazione durevole

I metodi EnlistDurable consentono di integrare un gestore di risorse affinché partecipi alla transazione come risorsa durevole. Un gestore di risorse durevoli è in grado di eseguire il ripristino anche se diventa non disponibile durante l'esecuzione di una transazione. Non appena diventa nuovamente disponibile, il gestore di risorse esegue il ripristino reintegrandosi (tramite il metodo Reenlist) in tutte le transazioni a cui partecipava e per cui non è stato in grado di completare la fase 2. Al termine delle operazioni di ripristino, il gestore di risorse chiama il metodo RecoveryComplete. Per ulteriori informazioni sul recupero, vedere Esecuzione del ripristino.

Tutti i metodi EnlistDurable accettano come primo parametro un oggetto Guid. L'oggetto Guid viene utilizzato dalla gestione transazioni per associare un'integrazione durevole a un determinato gestore di risorse. È pertanto estremamente importante che, quando riavviato, un gestore di risorse utilizzi in modo coerente lo stesso oggetto Guid per identificarsi anche presso più gestori di risorse. In caso contrario, è possibile che la procedura di ripristino abbia esito negativo.

Il secondo parametro del metodo EnlistDurable è un riferimento all'oggetto che il gestore di risorse implementa per ricevere notifiche transazionali. L'overload utilizzato consente di comunicare alla gestione transazioni se il gestore di risorse supporta l'ottimizzazione mediante SPC (Single-Phase Commit, commit monofase). La maggior parte delle volte si implementerebbe l'interfaccia IEnlistmentNotification per consentire la partecipazione al commit a due fasi. Tuttavia, se si desidera ottimizzare il processo di commit, è possibile implementare l'interfaccia ISinglePhaseNotification per il commit monofase. Per ulteriori informazioni sul protocollo SPC, vedere Commit di una transazione monofase e multifase e Ottimizzazione mediante commit monofase e notifica monofase promuovibile.

Il terzo parametro è un'enumerazione EnlistmentOptions il cui valore può essere None o EnlistDuringPrepareRequired. Se il valore è impostato su EnlistDuringPrepareRequired, l'integrazione può coinvolgere gestori di risorse aggiuntivi non appena viene ricevuta la notifica di preparazione inviata dalla gestione transazioni. Si noti tuttavia che per questo tipo di integrazione non è possibile utilizzare l'ottimizzazione mediante SPC.

Integrazione volatile

I partecipanti che gestiscono risorse volatili, ad esempio una cache, devono integrarsi mediante i metodi EnlistVolatile. Tali oggetti potrebbero non essere in grado di conoscere il risultato di una transazione o ripristinare lo stato delle transazioni a cui partecipano a seguito di un errore di sistema.

Come già descritto, i gestori di risorse eseguono un'integrazione volatile solo se gestiscono una risorsa in memoria volatile. Uno dei vantaggi dell'utilizzo del metodo EnlistVolatile è che consente di evitare le escalation non necessarie delle transazioni. Per ulteriori informazioni sull'escalation della gestione delle transazioni, vedere l'argomento Escalation della gestione delle transazioni. L'inserimento volatile differisce da quello durevole sia in termini di gestione dell'integrazione da parte della gestione transazioni sia in termini delle previsioni della gestione transazioni in merito al comportamento del gestore di risorse. Ciò è dovuto al fatto che il gestore di risorse volatile non esegue alcuna procedura di ripristino. Ciò a sua volta comporta che i gestori di risorse volatili non chiamano alcun metodo Reenlist a cui passare un oggetto Guid. Ne consegue che i metodi EnlistVolatile non accettano alcun parametro Guid.

Analogamente alle integrazioni durevoli, la gestione transazioni è in grado di stabilire se il gestore di risorse supporta l'ottimizzazione mediante il protocollo SPC a partire dal metodo di overload utilizzato per eseguire l'integrazione. Poiché i gestori delle risorse volatili non eseguono alcuna procedura di ripristino, la fase di preparazione dell'integrazione volatile non prevede alcuna scrittura di informazioni di ripristino. Se pertanto si chiama il metodo RecoveryInformation, il sistema genera un'eccezione InvalidOperationException.

Nell'esempio seguente viene mostrato come utilizzare il metodo EnlistVolatile per integrare un oggetto come partecipante a una transazione.

    Public Shared Sub Main()
        Try
            Using scope As TransactionScope = New TransactionScope()

                'Create an enlistment object
                Dim myEnlistmentClass As New EnlistmentClass

                'Enlist on the current transaction with the enlistment object
                Transaction.Current.EnlistVolatile(myEnlistmentClass, EnlistmentOptions.None)

                'Perform transactional work here.

                'Call complete on the TransactionScope based on console input
                Dim c As ConsoleKeyInfo
                While (True)
                    Console.Write("Complete the transaction scope? [Y|N] ")
                    c = Console.ReadKey()
                    Console.WriteLine()
                    If (c.KeyChar = "Y") Or (c.KeyChar = "y") Then
                        scope.Complete()
                        Exit While
                    ElseIf ((c.KeyChar = "N") Or (c.KeyChar = "n")) Then
                        Exit While
                    End If
                End While
            End Using
        Catch ex As TransactionException
            Console.WriteLine(ex)
        Catch
            Console.WriteLine("Cannot complete transaction")
            Throw
        End Try
    End Sub
End Class

Public Class EnlistmentClass
    Implements IEnlistmentNotification

    Public Sub Prepare(ByVal myPreparingEnlistment As PreparingEnlistment) Implements System.Transactions.IEnlistmentNotification.Prepare
        Console.WriteLine("Prepare notification received")

        'Perform transactional work

        'If work finished correctly, reply with prepared
        myPreparingEnlistment.Prepared()
    End Sub

    Public Sub Commit(ByVal myEnlistment As Enlistment) Implements System.Transactions.IEnlistmentNotification.Commit
        Console.WriteLine("Commit notification received")

        'Do any work necessary when commit notification is received

        'Declare done on the enlistment
        myEnlistment.Done()
    End Sub

    Public Sub Rollback(ByVal myEnlistment As Enlistment) Implements System.Transactions.IEnlistmentNotification.Rollback
        Console.WriteLine("Rollback notification received")

        'Do any work necessary when rollback notification is received

        'Declare done on the enlistment
        myEnlistment.Done()
    End Sub

    Public Sub InDoubt(ByVal myEnlistment As Enlistment) Implements System.Transactions.IEnlistmentNotification.InDoubt
        Console.WriteLine("In doubt notification received")

        'Do any work necessary when indout notification is received

        'Declare done on the enlistment
        myEnlistment.Done()
    End Sub
End Class
static void Main(string[] args)
{
    try
    {
        using (TransactionScope scope = new TransactionScope())
        {
        
            //Create an enlistment object
            myEnlistmentClass myElistment = new myEnlistmentClass();

            //Enlist on the current transaction with the enlistment object
            Transaction.Current.EnlistVolatile(myElistment, EnlistmentOptions.None);

            //Perform transactional work here.

            //Call complete on the TransactionScope based on console input
                            ConsoleKeyInfo c;
            while(true)
                            {
                Console.Write("Complete the transaction scope? [Y|N] ");
                c = Console.ReadKey();
                Console.WriteLine();
        
                                    if ((c.KeyChar == 'Y') || (c.KeyChar == 'y'))
                {
                    scope.Complete();
                    break;
                }
                else if ((c.KeyChar == 'N') || (c.KeyChar == 'n'))
                {
                    break;
                }
            }
        }
    }
    catch (System.Transactions.TransactionException ex)
    {
        Console.WriteLine(ex);
    }
    catch
    {
        Console.WriteLine("Cannot complete transaction");
        throw;
    }
}

class myEnlistmentClass : IEnlistmentNotification
{
    public void Prepare(PreparingEnlistment preparingEnlistment)
    {
        Console.WriteLine("Prepare notification received");

        //Perform transactional work

        //If work finished correctly, reply prepared
        preparingEnlistment.Prepared();

        // otherwise, do a ForceRollback
        preparingEnlistment.ForceRollback();
    }

    public void Commit(Enlistment enlistment)
    {
        Console.WriteLine("Commit notification received");

        //Do any work necessary when commit notification is received

        //Declare done on the enlistment
        enlistment.Done();
    }

    public void Rollback(Enlistment enlistment)
    {
        Console.WriteLine("Rollback notification received");

        //Do any work necessary when rollback notification is received

        //Declare done on the enlistment
        enlistment.Done();
    }

    public void InDoubt(Enlistment enlistment)
    {
        Console.WriteLine("In doubt notification received");

        //Do any work necessary when indout notification is received
        
        //Declare done on the enlistment
        enlistment.Done();
    }
}

Ottimizzazione delle prestazioni

La classe Transaction fornisce inoltre il metodo EnlistPromotableSinglePhase per eseguire l'integrazione PSPE (Promotable Single Phase Enlistment). Grazie a questo meccanismo, un gestore di risorse durevoli può ospitare e "possedere" una transazione di cui in seguito può eseguire l'escalation in modo che venga gestita dal gestore MSDTC, se necessario. Per ulteriori informazioni in merito, vedere Ottimizzazione mediante commit monofase e notifica monofase promuovibile.

Vedere anche

Concetti

Ottimizzazione mediante commit monofase e notifica monofase promuovibile
Commit di una transazione monofase e multifase