Enter the Connection Broker
The basic idea behind a connection broker is enabling you to roll your own library that centralizes and manages access to database resources (like the DTC does). The approach this article proposes defines two main classes that mediate the application code's access to the actual connection and transaction objects. It doesn't try to bring two or more connections into the same transaction as the DTC does.
The two classes are a
SmartConnection class and a
SmartTransaction class. The former implements
IDbConnection and the latter
IDbTransaction, so that they are (almost) indistinguishable from the classes the different .NET managed providers expose. Specifically, they will manage a single connectionand its associated transactionfor each execution scope. A proper algorithm will filter and monitor the access sequence to database resources, so that only the calls the logical root object places are actually forwarded to the underlying connection and transaction objects.
The business and data-access layers will use these two classes instead of the usual ones from .NET managed data providers. The
SmartConnection class holds an internal reference to an actual connection, and the
SmartTransaction class holds an internal reference to a transaction object. These classes blindly forward method calls via these internal references but for the
Rollback methods. Within these four methods, the two classes work their magic to behave as database connection and transaction brokers within a given execution scope.
Before delving into the implementation details, some explanation regarding the term "execution scope": it encompasses, in its widest sense, all the work that different threads running on separate processes or machines perform. In a generic scenario, the transaction flow is a logical thread of execution that spawns across two or more processes. For example, consider a transaction that starts when a client calls into component C1 running on thread T1 on process P1. If component C1 creates and calls into a remote object C2 running on process P2, component C2 won't of course share the same process or the same thread with the caller. However, C1 and C2 share the same logical thread of execution (see Figure 1). COM+ can actually trace such an execution pattern, and it guarantees that the DTC-based transaction will flow across the process (or network) boundaries.
This article doesn't cover the scenario above (even though it may come up in some large applications). Rather, it concentrates on the most common execution pattern in a three-layered application: the entire job performed by business and data objects that are hosted in a single process. The process hosting the business and data objects is a highly multithread environment, yet it guarantees thread isolation among objects activated by different clientsno matter which remoting technologies you choose (enterprise services, .NET remoting, or Web services). Each remote call executes within a random thread picked up from a thread pool (the .NET thread pool or the MTA thread pool) (see Figure 2).
Typically, the root business object executes its tasks by creating and calling into other business and data objects. If you don't explicitly create a new thread, these new objects will all share the same thread as the original one that got the remote execution call. As you can see, even in a highly multithread environment, a thread scope of execution is sufficient for typical three-layered applications. This is exactly the scope the example connection broker in this article will support.
|Author's Note: Because connection objects cannot be marshaled by reference across processes and computers, COM+-based transactions are a must for transactions spawning more than one process. In fact, you can't actually improve the proposed connection broker solution to handle this extended scenario.