RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Build a Framework for Managing Distributed Transactions in Three-tier Applications

For many transactional applications in .NET, you can avoid the overhead of using Enterprise Services and simply mimic the simple transactional style of COM+ applications by using a DistributedTransaction object that wraps an ADO.NET Transaction object.

ne of the best features of developing COM+ based applications in standard three-tier presentation, business, and data access (DAL) layers is the simplicity of managing transactions distributed between the application's layers. I'm not referring to the canonical definition of "distributed transactions"—that is "distributed between different databases," but to "distributed between a single application's different logical layers and methods."

Developing a transactional business process (such as inserting a new order, updating a user's data, activating a new account, etc.) in the COM+ environment is disarmingly linear. It requires:

  • A method in the business layer that implicitly begins a transaction.
  • Calling all the DAL or business layer's methods required to define the operation's flow.
  • Committing or rolling back the transaction initiated by the starting method.
The intermediate methods involved in the transaction (in general all DAL methods) are limited to calling DisableCommit on the execution's context, while only the starting method (the transaction's manager) may call a commit, if all methods involved in the transaction succeed or an abort if logical or runtime errors occur.

Unfortunately, you'll probably recall this clear and simple-to-implement three-part procedure with nostalgia when you switch to .NET. Yes, you can use the .NET Enterprise Services for transactions, but I think that using Enterprise Services adds complications that are worse than the benefits, so I prefer to use only ADO.NET resources.

ADO.NET does provide a good resource with its Transaction object, but by itself that doesn't permit you to implement cross-layer transactions in a simple fashion. That's not because it lacks functionality (if you try, you can approach the functionality of the steps described above), but because to achieve the goal you must create (and duplicate) similar code in every method involved in the transaction—in fact, in every business process layer and DAL method), which is a boring, expensive, and ultimately unaffordable solution.

To circumvent such problems this article describes a small transactional-support framework that you can use to write .NET code using the same three-step process described above that you formerly used in the COM+ environment—but without using Enterprise Services. The resulting applications require minimal code to manage transactions distributed between layers, and let you reuse methods to define different operations and flows that would otherwise prove expensive to maintain.

Framework Structure
This framework is based on the idea that it's possible to create a "DistributedTransaction" .NET object that you can use in the same manner as in classic COM+. This DistributedTransaction object is a wrapper built around the ADO.NET Transaction object, which exposes the fundamental functionality. But the new object results in simpler access to data, and makes distributed transactions available across all modules in a three-tier application.

The only formal difference when implementing solutions based on this framework as compared with COM+, is that now the business layer's method must always explicitly instantiate the transaction, and you must pass the DistributedTransaction object through calls that clearly are not executed inside a uniform context managed by an external environment, as in COM+. Otherwise, it's just as simple as using COM+; DAL methods work autonomously, and they can optionally call DisableCommit when a problem occurs. The business layer's methods may call Commit if all the operations succeed, or SetAbort, if logical or runtime errors occurred during flow's execution.

A Typical COM+ Application
Before analyzing the framework, here's a simple typical COM+ application, written in VB6, that defines a distributed transaction.

The DAL's method has its MTSTransactionMode property set to UsesTransaction or RequiresTransaction, depending on its logical characteristics, and it doesn't contain explicit code to manage transactions. To add error handling in the method, you can call GetObjectContext.DisableCommit; but if you're satisfied with having runtime errors handled by the caller, and not interested in registering the method's internal status (this is generally acceptable), you can simply ignore error-handling, and ignore any distributed transaction problems altogether.

The following business layer method initiates and completes the transaction. Its MTSTransactionMode property would be set to either RequiresNewTransaction or RequiresTransaction. The implementation inserts two code elements to manage the distributed transaction: a call to GetObjectContext.SetComplete that executes to commit the transaction, and a general error handler, that calls GetObjectContext.SetAbort to rollback the transaction if errors occur.

   Public Function DoSomething(
      ByVal inputData As Object) As Integer
      On Error GoTo ErrorHandler
      '' [...] Executes business operations
      '' - Checks for business rules
      If rulesValidationSuccess = False Then
         '' -- sets an error code to return
         DoSomething = -20
         ' -- rollback transaction
         Exit Function
      End If
      ' - Commits transaction
      ' [...] Releases objects and/or peforms non-transactional 
      ' operations (eg. sends an emails)
      ' -- manages global errors
      ' -- rolls back transaction
      '[...] Releases resources, logs error, etc.
   End Function

With just these few lines of code you can define all the business flows you need, using similar business layer or DAL methods. This structure is a lot simpler than the equivalent version made with standard .NET and ADO.NET base objects.

Framework Elements
The framework consists of an object that implements its logic and a pattern you can use to write business and data layer methods that exploit its benefits. But before defining that object, remember that a COM+ transaction's state is defined by two bits, called happy and done. The happy state represents a transaction that has not yet been committed—even if a method involved in the transaction has called DisableCommit, but no rollback has yet been executed. The done state represents a transaction that has been closed (either committed or rolled back), and is therefore no longer usable. Indeed, committing or rolling back a transaction releases all resources on the underlying database, and ends its lifecycle. In other words, if you still need to call other commands, you need to create and work with a new transaction context. With that in mind, here's how to create the DistributedTransaction wrapper object.

Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date