Extending Transaction Scope to Non-Database Transactions using Resource Manager

Implementing a reliable application which can roll back on failures is every organization’s need. .Net framework provides a built-in support for Transactions initiated in SQL Server, ADO.Net, MSMQ and MSDTC.

But, you might come across a situation where you need this capability in areas which do not fall in above, for example a transactional capability on files being created, or creation of entry in Active Directory.

Problem Scenario:

Let’s consider a business requirement where we have to create a user in the System.

The user creation requires the following steps in a single transaction.

  1. Create the entry in Database.
  2. Create the entry in Active Directory.
  3. Create the File/Folder in File System.

If any of the above operation fails the system must rollback the entire transaction. We can roll back the database operation easily, but unable to roll back the Active Directory and File-Folder operations.

So there is a need to manage the non-Database operations in a transactional manner.

Analyze Resolution to Problem Scenario:

.Net Framework provides an extensible framework to maintain the resources by creating the custom resource manager. The RM manages the reliability of resource whose actions are monitored by Transaction manager. Resource managers work in cooperation with the transaction manager to provide the atomicity and isolation

We have to create a resource manager to enlist the resource to participate in Two Phase Commit (2PC) by implementing the IEnlistmentNotification interface for the resource manager.

By enlisting, the resource manager ensures that it gets callbacks from the transaction manager when the transaction commits or aborts. After enlistment, the resource manager responds to the transaction’s requests.

When the application commits the transaction, the transaction manager initiates the two-phase commit protocol. The transaction manager first asks each enlisted resource manager if it is prepared to commit the transaction. Once prepared, a resource manager must wait until it gets a commit or abort callback from the transaction manager in phase 2. In summary, the two-phase commit protocol and the resource managers combine to make transactions atomic and durable.

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 the failure of the commit to the application.

Implementation Strategy to resolve the problem scenario:

To resolve the problem we need to create a Resource Manager (RM) for each resource (Active Directory and File system). We used the IEnlistmentNotification interface for the resource manager and called the EnlistVolatile method to enlist the resource in Transaction.

So we would have all the necessary RMs in same transaction. If any operation fails, TM call the Rollback method from all the RMs and thus business maintain the atomicity.

Code to resolve the problem scenario:

//Code in Business Layer

using (TransactionScope tsCompanyCreate = newTransactionScope(TransactionScopeOption.Required, TransactionManager.MaximumTimeout))

{

//DB operations

//Active Directory Operation

MyADManager instanceADUser=new MyADManager();

instanceADUser.CreateUserInAD ();

Folder Creation Operation

}

// Class which call the RM to enlist in Transaction.

public class MyADManager

{

private static Dictionary<string, ADRM> _enlstsAD;

private static readonly object _enlstADLock = new object();

public DirectoryEntry CreateUserInAD()

{

DirectoryEntry user = EnlistADRMInTrans();

return user;

}

private static DirectoryEntry EnlistADRMInTrans()

{

Transaction tx = Transaction.Current;

ADRM enlistmentAD;

DirectoryEntry user = null;

lock (_enlstADLock)

{

if (_enlstsAD == null)

{

_enlstsAD = new Dictionary<string, ADRM>();

}

if (!_enlstsAD.TryGetValue(tx.TransactionInformation.LocalIdentifier, out enlistmentAD))

{

//enlist the RM for AD in current transaction.

enlistment = new ADRM(tx);

//Get a unique identifier of the transaction.

_enlstsAD.Add(tx.TransactionInformation.LocalIdentifier, enlistmentAD);

}

user = enlistment.CreateADResourceByRM ();

}

return user;

}

}

//Resource Manager for Active Directory.

sealed class ADRM: IEnlistmentNotification

{

//enlist the volatile resource

public ADRM(Transaction txMainTransaction)

{

//enlist the resource in transaction.

txMainTransaction.EnlistVolatile(this, EnlistmentOptions.None);

}

public DirectoryEntry CreateADResourceByRM()

{

//create user in AD

return user;

}

public void Commit(Enlistment enlistment)

{

enlistment.Done();

}

public void InDoubt(Enlistment enlistment)

{

Rollback(enlistment);

}

public void Prepare(PreparingEnlistment preparingEnlistment)

{

preparingEnlistment.Prepared();

}

public void Rollback(Enlistment enlistment)

{

//rollback the user.

enlistment.Done();

}

}

References

http://msdn.microsoft.com/en-us/library/system.transactions.aspx

One thought on “Extending Transaction Scope to Non-Database Transactions using Resource Manager

Leave a Reply

Your email address will not be published. Required fields are marked *


6 × = forty two

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>