It's Spann, not spam

Technical blog to provide content that developers find useful.

Transaction Around Non-Transaction Pieces

Transaction Around Non-Transaction Pieces

  • Comments 3

Modern databases deal with transactions seamlessly.  But what do you do when you want an entry added/removed to/from a database table after you perform some task on a non-transactional portion of your system, say Active Directory or even the file system?  Well the answer depends on the order in which you have to do things.  So if you can Insert a record into a database table then create/delete a user from Active Directory then you can use TransactionScope in the .NET Framework 2.0.  On the other hand if you cannot get around the order in which things have to occur, then you have to do your own management of transactions.

Background of TransactionScope Class

The

System.Transactions infrastructure provides both an explicit programming model based on the Transaction class, as well as an implicit programming model using the TransactionScope class, in which transactions are automatically managed by the infrastructure.

NoteImportant:

It is recommended that you create implicit transactions using the TransactionScope class, so that the ambient transaction context is automatically managed for you. You should also use the TransactionScope and DependentTransaction class for applications that require the use of the same transaction across multiple function calls or multiple thread calls. For more information on this model, see the Implement Implicit Transactions using Transaction Scope topic. For more information on writing a transactional application, see Writing A Transactional Application.

Upon instantiating a TransactionScope by the new statement, the transaction manager determines which transaction to participate in. Once determined, the scope always participates in that transaction. The decision is based on two factors: whether an ambient transaction is present and the value of the TransactionScopeOption parameter in the constructor. The ambient transaction is the transaction your code executes in. You can obtain a reference to the ambient transaction by calling the static Current property of the Transaction class. For more information on how this parameter is used, please see the "Transaction Flow Management" section of the Implement Implicit Transactions using Transaction Scope topic.

If no exception occurs within the transaction scope (that is, between the initialization of the TransactionScope object and the calling of its Dispose method), then the transaction in which the scope participates is allowed to proceed. If an exception does occur within the transaction scope, the transaction in which it participates will be rolled back.

When your application completes all work it wants to perform in a transaction, you should call the Complete method only once to inform that transaction manager that it is acceptable to commit the transaction. Failing to call this method aborts the transaction.

A call to the Dispose method marks the end of the transaction scope. Exceptions that occur after calling this method may not affect the transaction.

If you modify the value of Current inside a scope, an exception is thrown when Dispose is called. However, at the end of the scope, the previous value is restored. In addition, if you call Dispose on Current inside a transaction scope that created the transaction, the transaction aborts at the end of the scope.

Code Example

using (TransactionScope ts = new TransactionScope())
{
     ApplicationRoleDao.RevokeUserAccess(applicationRoleId, userId);

     // Remove the user from the AD group
     System.DirectoryServices.DirectoryEntry deGroups = Account.GetDirectoryEntry(pathGroups);
     System.DirectoryServices.DirectoryEntry deUsers = Account.GetDirectoryEntry(pathUsers);
     string groupName = ApplicationRoleDao.GetActiveDirectoryGroup(applicationRoleId);
     if (!string.IsNullOrEmpty(groupName))
     {
          Account.RemoveUserFromGroup(deGroups, Account.GetUserFromDirectory(deUsers, userId), groupName);
     }

     ts.Complete();
}

So the code above will first remove the user from the database.  Then it will attempt to remove the user from Active Directory.  If either action throws an exception, the transaction in the database will roll back and the user will still be in the system.

  • PingBack from http://wordnew.acne-reveiw.info/?p=8428

  • You should clarify the following three requirements:

    1.  There is only one non-transacted action in the 'using' block

    2.  The non-transacted action must be the last item in the 'using' block

    3.  The non-transacted action fully and cleanly rolls itself back when it fails

    Also, I think that in theory (although perhaps unlikely in reality) even your code above could behave unexpectedly.  For example, suppose everything in the 'using' block completes, but the 'using' block itself has not yet run its 'dispose' logic.  Then an exception occurs - for example, if another thread aborts this one, a ThreadAbortedException will be generated from the current execution location.  In that scenario, the user will still be removed from the group, but the revocation will get rolled back.

  • Bruce,

    Your three points are correct.  Sorry about the omission.  I should have explained that a little further.

    In theory you would be correct.  Because AD is not tranactional in nature, this is the best we can expect at the moment.  I believe that in the future, AD will participate in DTC.  When that happens the above code will no longer need to be executed.  We will just allow the TransactionScope to take care of it for us.  Proper testing of this kind of code will go a long way!

    Happy Coding,

    Brian

Page 1 of 1 (3 items)
Leave a Comment
  • Please add 5 and 8 and type the answer here:
  • Post