How do i enlist in an ongoing TransactionScope?
If you use a TransactionScope, you can create an "ambient" transaction:
using (TransactionScope scope = new TransactionScope())
{
//...stuff happens, then you complete...
// The Complete method commits the transaction.
scope.Complete();
}
What is that good for? Well, there are some classes in the .NET framework that know how to check if there is an ambient ongoing transaction by checking the static
:
This lets them know that there is a transaction in progress.
For example, if you had some arbitrary database operation that otherwise didn't consider transactions:
void DropStudents()
{
using (var cmd = connection.CreateCommand())
{
cmd.CommandText = "DROP TABLE Students;";
cmd.ExecuteNonQuery();
}
}
If you plop that in the middle of a TransactionScope:
using (TransactionScope scope = new TransactionScope())
{
DropStudents();
// The Complete method commits the transaction.
scope.Complete();
}
Suddenly your ADO.net operations are in a transaction; and can be rolled back.
The SqlClient library knows to check:
and automatically internally:
And it's all just magic.
DbConnection
gets a notification to call .Commit
DbConnection
gets a notification to call .Rollback
I have a class that also has transactions. And rather than forcing the caller to call:
using (IContosoTransaction tx = turboEncabulator.BeginTransaction())
{
try
{
turboEncabulator.UpendCardinalGrammeters();
}
catch (Exception ex)
{
tx.Rollback();
throw;
}
tx.Commit();
}
It would be nice if they could just call:
turboEncabulator.UpendCardinalGrammeters();
And my code will simply check:
And if there is a transaction in progress, i will:
But how do i do that?
How do i register myself with an ongoing TransactionScope to get these notifications?
It's not that bad actually.
Check if there is a transaction in progress by seeing if System.Transactions.Transaction.Current
is assigned. If there is, Enlist
in the transaction:
//Enlist in any current transactionScope if one is active
if (System.Transactions.Transaction.Current != null)
System.Transactions.Transaction.Current.EnlistVolatile(this, EnlistmentOptions.None);
I pass this
, as the object that will get the notification callbacks through the IEnlistmentNotification
interface that it must implement.
And then you need to implement the four notification methods of IEnlistmentNotification
:
void Prepare(PreparingEnlistment preparingEnlistment);
void Commit(Enlistment enlistment);
void InDoubt(Enlistment enlistment);
void Rollback(Enlistment enlistment);
The actual implementations are trivial boilerplate notifications:
preparingEnlistment.Prepared();
preparingEnlistment.ForceRollback();
enlistment.Done();
enlistment.Done();
enlistment.Done();
public void Prepare(PreparingEnlistment preparingEnlistment)
{
//The transaction manager is asking for our vote if the transaction
//can be committed
//Vote "yes" by calling .Prepared:
preparingenlistment.Prepared();
//Vote "no" by calling .ForceRollback:
//preparingEnlistment.ForceRollback();
}
public void Commit(Enlistment enlistment)
{
//The transaction is being committed - do whatever it is we do to commit.
//Let them know we're done with the enlistment.
enlistment.Done();
}
public void InDoubt(Enlistment enlistment)
{
//Do any work necessary when indoubt notification is received.
//This method is called if the transaction manager loses contact with one or more participants,
//so their status is unknown.
//If this occurs, you should log this fact so that you can investigate later whether any of the
//transaction participants has been left in an inconsistent state.
//Let them know we're done with the enlistment.
enlistment.Done();
}
public void Rollback(Enlistment enlistment)
{
//If any resource manager reported a failure to prepare in phase 1, the transaction manager invokes
//the Rollback method for each resource manager and indicates to the application the failure of the commit.
//Let them know we're done with the enlistment.
enlistment.Done();
}