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


Control Transaction Boundaries Between Layers : Page 3

A connection broker enables your business layer to control transaction boundaries in a compact and efficient way. Learn how to roll your own broker to centralize and manage your application code's access to database resources.

Implementation Details
The downloadable code accompanying this article provides the entire implementation for the SmartConnection and SmartTransaction classes, along with an AuthorsBooks business object and a couple of data objects (DoAuthors and DoBooks). Note that the Author-Book business entity is implemented using an enhanced Dataset, which encapsulates validation logic into itself.

Let's examine the SmartConnection class. To separate database resources at the thread level, mark the underlying connection object that the SmartConnection class manages by using the ThreadStatic attribute. One would be tempted to write code such as the following:

[ThreadStatic()] private static IDbConnection ms_RealConn = new SqlConnection ();

This won't work since the CLR assigns the required value in only the first thread referencing the ms_RealConn variable. Whether this is a bug or not, you can workaround the issue by modifying the code as shown below. Additionally, make sure that no code accesses the ms_RealConn_private variable directly, but rather through the ms_RealConn property getter:

[ThreadStatic()] private static IDbConnection ms_RealConn_private ; private static IDbConnection ms_RealConn { get { if (ms_RealConn_private == null) ms_RealConn_private = new SqlConnection (); return ms_RealConn_private; } }

No synchronization is required since the variable has a thread scope. In the downloadable code included with this article, the SmartConnection class uses another framework-level component named DataAccess, which returns only provider-independent resources (i.e., IDbConnection instead of, say, SqlConnection). As shown in the code snippet below, a basic implementation could use a ThreadStatic counter so that in the Open method the connection is opened only when the counter is zero. Likewise, the Close method implementation closes the underlying connection only if the counter is equal to one:

public class SmartConnection : IDbConnection { ... public void Open() { if(ms_counter ==0) ms_RealConn.Open (); ms_counter +=1; } public void Close() { if(ms_counter ==1) ms_RealConn.Close(); ms_counter -=1; } ... }

The SmartTransaction class would handle transactions in a similar way. This approach has a serious drawback, however: you have no way to detect when a business or data object forgets, for example, to call Close after it called Open or, even worse, to call Commit (or rollback) once it called BeginTransaction. In the latter case, the effects are a really disastrous. For example, the root object could call Commit, but the transaction wouldn't actually commit since the counter is not equal to 1, meaning the whole job is doomed to be "rollbacked" by a transaction timeout later on without any notification to the client.

To overcome this issue, replace the simple counter with a Stack object and assign a Guid to each SmartConnection instance to uniquely identify it. In the Open method, instead of incrementing the counter, push the specific SmartConnection Guid to the Stack:

public class SmartConnection : IDbConnection { ... public void Open() { if(ms_RealConnStack.Count ==0) ms_RealConn.Open (); ms_RealConnStack.Push(m_guid); } ...

In the Close method's implementation you will try to match the Guid of the instance asking the connection to close with the Guid present at the top of the Stack. If they match, everything is okay, and if the Stack is empty once you've popped the Guid, you close the underlying connection. If the Guid doesn't match, the method raises an exception to signal that a child object called Open but forgot to call Close. Essentially, it's just like pairing opening and closing brackets in a programming language. The following code shows the implementation of the Close method of the SmartConnection class:

public class SmartConnection : IDbConnection { ... public void Close() { if((Guid)(ms_RealConnStack.Peek())!= m_guid) throw new Exception ( "Invalid Sequence"); ms_RealConnStack.Pop(); if(ms_RealConnStack.Count ==0) ms_RealConn.Close(); } ... }

The implementation of the BeginTransaction, Commit, and Rollback methods follows the same logic to detect unmatched calls and sequence errors regarding the transaction start and outcome.

One of the last aspects to handle is detecting an incorrect call sequence within the same SmartConnection instance, such as when BeginTransaction is called before Open or Open is called twice. You can easily account for this problem by defining an instance-level ConnBrokerStatusEnum enum value that the broker checks before manipulating the ThreadStatic objects (see Listing 1). The SmartTransaction's ms_doomed flag prohibits a parent object from calling Commit if a child object has called RollBack.

Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.



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