File: Hosting\LocalTransaction.cs
Project: ndp\cdf\src\WF\RunTime\System.Workflow.Runtime.csproj (System.Workflow.Runtime)
//------------------------------------------------------------------------------
// <copyright file="LocalTransaction.cs" company="Microsoft">
//   Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
 
#region Using directives
 
using System;
using System.Diagnostics;
using System.Transactions;
using System.Data;
using System.Data.Common;
using System.Threading;
 
#endregion
 
namespace System.Workflow.Runtime.Hosting
{
    /// <summary>
    /// This class wraps a local transaction (DbTransaction) by implementing IPromotableSinglePhaseNotification:
    /// It instantiate a DbTransaction and never allows promotion to a DTC transaction.
    /// </summary>
    internal sealed class LocalTransaction : IPromotableSinglePhaseNotification
    {
        readonly DbTransaction transaction;
        System.Data.Common.DbConnection connection;
        ManualResetEvent handle;
        object syncRoot = new Object();
 
        #region Constructor
 
        /// <summary>
        /// Wraps a local transaction inside
        /// </summary>
        /// <param name="dbHelper"></param>
        internal LocalTransaction(DbResourceAllocator dbHelper, ManualResetEvent handle)
        {
            if (null == handle)
                throw new ArgumentNullException("handle");
 
            // Open a connection that specifically does not auto-enlist ("Enlist=false" in connection string)
            // to prevent auto-promotion of the transaction
            this.connection = dbHelper.OpenNewConnectionNoEnlist();
            this.transaction = this.connection.BeginTransaction();
            this.handle = handle;
        }
 
        #endregion Constructor
 
 
        #region Accessors
 
        public System.Data.Common.DbConnection Connection
        {
            get { return this.connection; }
        }
 
        public DbTransaction Transaction
        {
            get { return this.transaction; }
        }
 
        #endregion Accessors
 
 
        #region IPromotableSinglePhaseNotification Members
 
        public void Initialize()
        {
        }
 
        public void SinglePhaseCommit(SinglePhaseEnlistment en)
        {
            if (en == null)
                throw new ArgumentNullException("en");
 
            //
            // Wait until IPendingWork members have completed (WinOE bugs 17580 and 13395)
            try
            {
                handle.WaitOne();
            }
            catch (ObjectDisposedException)
            {
                // If an ObjectDisposedException is thrown because
                // the WaitHandle has already closed, nothing to worry
                // about. Move on.
            }
 
            lock (syncRoot)
            {
                try
                {
                    this.transaction.Commit();
                    en.Committed();
                }
                catch (Exception e)
                {
                    en.Aborted(e);
                }
                finally
                {
                    if ((null != connection) && (ConnectionState.Closed != connection.State))
                    {
                        connection.Close();
                        connection = null;
                    }
                }
            }
        }
 
        public void Rollback(SinglePhaseEnlistment en)
        {
            if (en == null)
                throw new ArgumentNullException("en");
 
            //
            // Wait until IPendingWork members have completed (WinOE bugs 17580 and 13395)
            try
            {
                handle.WaitOne();
            }
            catch (ObjectDisposedException)
            {
                // If an ObjectDisposedException is thrown because
                // the WaitHandle has already closed, nothing to worry
                // about. Move on.
            }
            // DbTransaction.Dispose will call Rollback if the DbTransaction is not Zombied.
            // Not safe to call DbTransaction.Rollback here as the the underlining 
            // DbTransaction may have already been Rolled back
            lock (syncRoot)
            {
                if (null != transaction)
                    transaction.Dispose();
 
                if ((null != connection) && (ConnectionState.Closed != connection.State))
                {
                    connection.Close();
                    connection = null;
                }
 
                en.Aborted();
            }
        }
 
        public byte[] Promote()
        {
            throw new TransactionPromotionException(
                ExecutionStringManager.PromotionNotSupported);
        }
 
        #endregion IPromotableSinglePhaseNotification Members
    }
}