System.Transactions in .NET 2.0

In .NET 2.0 Microsoft is adding support for the explicit creation and management of distributed database transactions in a new super-easy-to-use API (Application Programming Interface) called System.Transactions. As a bit of background, here's a grossly oversimplified history of distributed transaction APIs and frameworks:

  • In the beginning, nearly all transactional APIs are procedural in nature and require the explicit creation and destruction of transaction contexts by the caller. This type of code is rather difficult to write and maintain, especially in the case of distributed transactions that span multiple database servers or resource managers.
  • Microsoft introduces Microsoft Transaction Server (MTS) in 1996 which is one of the first mainstream frameworks (if not the very first) to provide automatic / declarative transaction support for distributed transactions. MTS achieves its simplicity by mapping transactional semantics directly onto COM method calls... allowing any COM implementation language (VB, C/C++, Delphi, etc) to play along.
  • Microsoft spends the next 5 to 7 years improving this technology and finally getting things working quite well... culminating in the present day COM+ model.
  • During this time, the Enterprise Java Beans spec imports the MTS / COM+ model of declarative transactions into the Java world.
  • Continuing the back and forth Sun / Microsoft technology borrow-a-thon of the late 20th century that has many interesting chapters... Microsoft starts with the general architecture of Java, adds several nice additions and enhancements and creates the .NET Framework.
  • .NET 1.0 includes (via System.EnterpriseServices) the closest realization to date of the original MTS vision of simple object-oriented transactions. In fact, the entire custom attribute system of .NET, which has many great uses totally unrelated to transactions, appears to be largely motivated by the MTS / COM+ requirements.
  • Microsoft adds Services without Components to COM+ and subsequently introduces its managed counterpart, System.Transactions, in .NET 2.0 which allows developers to explicitly create and destroy transactions manually, without relying on any MTS / COM+ object mapping. So... 8 to 10 years and 700 billion "How to program with DTC / MTS / COM+" book orders later... and we're back to almost exactly where we started.

Not that this is a bad thing... quite the opposite in fact. The MTS / COM+ style and the explicit transaction style have distinct feature sets and even in the cases where those feature sets overlap, both models have their pros and cons. Regardless, it's still pretty interesting to observe the full circle that Microsoft has completed here.

Although System.Transactions is, in some ways, a step backward in time, it is also an interesting and significant step forward in terms of developer productivity. Part of the original motivation for MTS / COM+ was the fact that explicit transaction code that included proper connection management and full error handling / rollback code, was quite tedious and verbose to create and maintain in the VB / C++ world of the mid 90s.

Where MTS / COM+ was revolutionary, System.Transactions is evolutionary. Highly evolutionary... so much so that System.Transactions looks almost nothing like its primitive ancestors. By relying on modern data access layers, a garbage collection runtime and even some of the post-Java features of .NET, new code written to System.Transactions is some of the cleanest and most readable distributed transaction code that I've ever seen:

public class AccountTransactions
{
    public static void Transfer(int IN_Amount)
    {
        using (TransactionScope scope = new TransactionScope())
        {
            using (SqlConnection connection1 = new SqlConnection(DemoData.Bank1Database))
            {
                connection1.Open();
                using (SqlConnection connection2 = new SqlConnection(DemoData.Bank2Database))
                {
                    connection2.Open();
                    DoWithdrawal(connection1, IN_Amount);
                    DoDeposit(connection2, IN_Amount);
                    scope.Complete();
                }
            }
        }
    }
}

This code is based on example code from Mike Clark's recent and highly recommended MSDN Video 'Introducing System.Transactions in .NET 2.0'.

Now before anyone jumps up and screams "That doesn't look any cleaner than the JDBC / ADO.NET / etc. code that I'm already using today" please note that this code implements a full distributed transaction across two distinct database servers. The databases in question could be on different machines or be of completely different platform / resource manager type (two Oracle servers, or one SQL Server and one Oracle Server, or a message queue and a database server, etc.)

The level of effort necessary to explicitly orchestrate these types of distributed transactions has never been lower. And if that wasn't enough... the new framework also automatically supports promotable enlistment (when supported by the underlying resource manager) which significant improves performance in situations where only a single database is being accessed. System.Transactions looks like it's going to be a great new addition to the .NET data access story.