Enlisting Resources as Participants in a Transaction
Each resource participating in a transaction is managed by a resource manager, whose actions are coordinated by a transaction manager. The coordination is done through notifications given to subscribers who have enlisted in a transaction through the transaction manager.
This topic covers how a resource (or multiple resources) can be enlisted in a transaction, as well as the different types of enlistment. The Committing a Transaction in Single-Phase and Multi-Phase topic covers how transaction commitment can be coordinated among enlisted resources.
Enlisting Resources in a Transaction
In order for a resource to participate in a transaction, it must enlist in the transaction. The Transaction class defines a set of methods whose names begin with Enlist that provide this functionality. The different Enlist methods correspond to the different types of enlistment that a resource manager may have. Specifically, you use the EnlistVolatile methods for volatile resources, and the EnlistDurable method for durable resources. The durability (or conversely the volatility) of a resource manager refers to whether the resource manager supports failure recovery. If a resource manager supports failure recovery, it persists data to durable storage during Phase1 (prepare) such that if the resource manager goes down, it can re-enlist in the transaction upon recovery and perform the proper actions based on the notifications received from the TM. In general, volatile resource managers manage volatile resources such as an in-memory data structure (for example, an in-memory transacted-hashtable), and durable resource managers manage resources that have a more persistent backing store (for example, a database whose backing store is disk).
For simplicity, after deciding whether to use the EnlistDurable or EnlistVolatile method based on your resource's durability support, you should enlist your resource to participate in Two Phase Commit (2PC) by implementing the IEnlistmentNotification interface for your resource manager. For more information on 2PC, see Committing a Transaction in Single-Phase and Multi-Phase.
A single participant can enlist for more than one of these protocols by calling EnlistDurable and EnlistVolatile multiple times.
Durable Enlistment
The EnlistDurable methods are used to enlist a resource manager for participation in the transaction as a durable resource. It is expected that if a durable resource manager is brought down in the middle of a transaction, it can perform recovery once it is brought back online by reenlisting (using the Reenlist method) in all transactions in which it was a participant and did not complete phase 2, and call RecoveryComplete once it finishes recovery processing. For more information on recovery, see Performing Recovery.
The EnlistDurable methods all take a Guid object as their first parameter. The Guid is used by the transaction manager to associate a durable enlistment with a particular resource manager. As such, it is imperative that a resource manager consistently uses the same Guid to identify itself even across different resource managers upon restarting, otherwise the recovery can fail.
The second parameter of the EnlistDurable method is a reference to the object that the resource manager implements to receive transaction notifications. The overload you use informs the transaction manager whether your resource manager supports the Single Phase Commit (SPC) optimization. Most of the time you would implement the IEnlistmentNotification interface to take part in Two-Phase Commit (2PC). However, if you want to optimize the commit process, you can consider implementing the ISinglePhaseNotification interface for SPC. For more information on SPC, see Committing a Transaction in Single-Phase and Multi-Phase and Optimization using Single Phase Commit and Promotable Single Phase Notification.
The third parameter is an EnlistmentOptions enumeration, whose value can be either None or EnlistDuringPrepareRequired. If the value is set to EnlistDuringPrepareRequired, the enlistment may enlist additional resource managers upon receiving the Prepare notification from the transaction manager. However, you should be aware that this type of enlistment is not eligible for the Single Phase Commit optimization.
Volatile Enlistment
Participants managing volatile resources such as a cache should enlist using the EnlistVolatile methods. Such objects might not be able to obtain the outcome of a transaction or recover the state of any transaction they participate in after a system failure.
As stated previously, a resource manager would make a volatile enlistment if it manages an in-memory, volatile resource. One of the benefits of using EnlistVolatile is that it does not force an unnecessary escalation of the transaction. For more information on transaction escalation, see Transaction Management Escalation topic. Enlisting volatilely implies both a difference in how the enlistment is handled by the transaction manager, as well as what is expected of the resource manager by the transaction manager. This is because a volatile resource manager does not perform recovery. The EnlistVolatile methods do not take a Guid parameter, because a volatile resource manager does not perform recovery and would not call the Reenlist method which needs a Guid.
As with durable enlistments, whichever overload method you use to enlist denotes to the transaction manager whether your resource manager supports the Single Phase Commit optimization. Since a volatile resource manager cannot perform recovery, no recovery information is written for a volatile enlistment during the Prepare phase. Therefore, calling the RecoveryInformation method results in an InvalidOperationException.
The following example shows how to enlist such an object as a participant in a transaction using the EnlistVolatile method.
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();
}
}
Optimizing Performance
The Transaction class also provides the EnlistPromotableSinglePhase method to enlist a Promotable Single Phase Enlistment (PSPE). This allows a durable resource manager (RM) to host and "own" a transaction that can later be escalated to be managed by the MSDTC if necessary. For more information on this, see Optimization using Single Phase Commit and Promotable Single Phase Notification.
See Also
Concepts
Optimization using Single Phase Commit and Promotable Single Phase Notification
Committing a Transaction in Single-Phase and Multi-Phase