Integrazione di risorse come partecipanti a una transazione
Ogni risorsa che partecipa a una transazione viene gestita dalla gestione risorse, le cui azioni vengono coordinate dalla 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 in monofase e multifase illustra come è possibile coordinare il commit delle transazioni tra 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 altre informazioni su 2PC, vedere Commit di una transazione in 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 altre informazioni sul ripristino, 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 altre informazioni su SPC, vedere Commit di una transazione in 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 altre informazioni sull'escalation delle transazioni, vedere l'argomento Escalation della gestione delle transazioni. L'integrazione della volatilità presenta differenze sia in termini di gestione dell'integrazione da parte del gestore delle transazioni, sia in termini di aspettative sul gestore di risorse da parte del gestore delle transazioni. 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 EnlistVolatile a cui passare un oggetto Guid. Ne consegue che i metodi Reenlist 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.
static void Main(string[] args)
{
try
{
using (TransactionScope scope = new TransactionScope())
{
//Create an enlistment object
myEnlistmentClass myEnlistment = new myEnlistmentClass();
//Enlist on the current transaction with the enlistment object
Transaction.Current.EnlistVolatile(myEnlistment, 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 in doubt notification is received
//Declare done on the enlistment
enlistment.Done();
}
}
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 in doubt notification is received
'Declare done on the enlistment
myEnlistment.Done()
End Sub
End Class
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 altre informazioni su questo argomento, vedere Ottimizzazione mediante commit monofase e notifica monofase promuovibile.