posted on Saturday, February 12, 2005 3:40 PM by taylorza

Distributed Transactions without COM+ Components

Update: I have posted a draft of an article that takes this topic further and addresses flowing distributed transactions. See the post at http://dotnetjunkies.com/WebLog/chris.taylor/articles/54503.aspx.

Not being in a position to wait for all the wonderful support for transactions in .NET Framework 2.0 namespace System.Transactions I have had to make use of existing services to enlist in Distributed transactions and further more to propagate those transactions across process and machine boundaries.

Initial solutions required the development of numerous COM+ components. Being a C++ developer with extensive COM experience this did not bother me but fortunately with COM+ 1.5’s Services Without Components I no longer needed jump these hoops to satisfy the transactional requirements of the software. Even better is .NET Framework 1.1 support for these services in the System.EnterpriseServices namespace. Through the next few posts I will be passing on some of the techniques I have used to propagate distributed transaction across WebServices and .NET Remoting. To kick start here is a watered down version of the TransactionContext class.

   1:    using System;
   2:    using System.EnterpriseServices;
   3:    using System.Runtime.InteropServices;
   4:   
   5:    public class TransactionContext : IDisposable
   6:    {
   7:      private bool _consistent = false;
   8:   
   9:      public TransactionContext() : this(TransactionOption.Required)
  10:      {
  11:      }
  12:   
  13:      public TransactionContext(TransactionOption option)
  14:      {
  15:        ServiceConfig config = new ServiceConfig();
  16:        config.Transaction = option;
  17:        config.TrackingEnabled = true;
  18:        ServiceDomain.Enter(config);
  19:      }
  20:   
  21:      public void Complete()
  22:      {
  23:        _consistent = true;
  24:      }
  25:   
  26:      public void Dispose()
  27:      {
  28:        try
  29:        {
  30:          GC.SuppressFinalize(this);
  31:          if (_consistent)
  32:            ContextUtil.SetComplete();
  33:          else
  34:            ContextUtil.SetAbort();
  35:        }
  36:        finally
  37:        {
  38:          ServiceDomain.Leave();  
  39:        }
  40:      }
  41:   
  42:      ~TransactionContext()
  43:      {
  44:        System.Diagnostics.Debug.Fail("TransactionContext must de explicitly Disposed");
  45:      }
  46:    }
 

To use this class is relatively simple, all you do is create an instance of the TransactionContext and if everything completes without causing a problem you put the context into a consistent state by calling Complete() when the Dispose method is called the vote for the success of the transaction is cast based on the consistency flag, you must ensure that Dispose is called as soon as your work is complete. The following example demonstrates the use of the TransactionContext class.

   1:        SqlConnection oCon;
   2:        SqlCommand oCmd;
   3:        using (TransactionContext tx = new TransactionContext())
   4:        {  
   5:          oCon = new SqlConnection("Your Connection String here");
   6:          oCmd = oCon.CreateCommand();
   7:          oCmd.CommandText = "insert into ...";
   8:          
   9:          oCon.Open(); 
  10:          oCmd.ExecuteNonQuery();
  11:          oCon.Close();
  12:   
  13:          tx.Complete();
  14:        }
 
In a future post I will address some of the more naïve solutions to propagate these transactions across process and machine boundaries.

Comments