static void

NHibernate Transaction wrapper

A simple IDisposable wrapper around NHibernate's ITransaction, for use inside NHibernate repositories. In practice, normal TransactionScope works well.

using System;
using NHibernate;
 
namespace Northwind.Repositories
{
    /// <summary>
    /// Ensure a code block is transactional.
    /// If the session transaction is not open, create a transaction; otherwise there is an existing transaction so don't do anything.
    /// </summary>
    /// <remarks>
    /// Equivalent to <see cref="System.Transactions.TransactionScope"/> with default <see cref="System.Transactions.TransactionScopeOption"/> value <c>Required</c> (enlist in enclosing transaction or create a new one if it doesn't exist).
    /// </remarks>
    public sealed class TransactionRequired : IDisposable
    {
        private const string TRANSACTIONKEY = "NHIBERNATE.TRANSACTION";
        private ITransaction _transaction;
        private bool _shouldCommit;
        private bool _completed;
 
        #region Constructor
        public TransactionRequired(ISession session)
        {
            if (session == null) throw new ArgumentNullException("session");
            _transaction = session.Transaction; //equal to Transaction.Current
            if (!IsOpenTransaction(_transaction))
            {
                _transaction = session.BeginTransaction();
                ShouldCommit = true;
            }
        }
        #endregion
 
        #region NHibernate Transactions
 
        /// <summary>
        /// Gets or sets a value indicating whether this transaction should commit. If there is an open transaction, by default calling Commit will not do anything- it will leave the transaction open.
        /// </summary>
        /// <value><c>true</c> if should commit; otherwise, <c>false</c>.</value>
        public bool ShouldCommit
        {
            get { return _shouldCommit; }
            set { _shouldCommit = value; }
        }
 
        public void Commit()
        {
            if (!ShouldCommit) return;
 
            if (_completed)
                throw new InvalidOperationException("The current transaction is already committed. You should dispose the transaction.");
 
            _completed = true;
 
            try
            {
                if (IsOpenTransaction(_transaction))
                {
                    _transaction.Commit();
                    _transaction = null;
                }
            }
            catch (HibernateException)
            {
                RollbackTransaction();
                throw;
            }
        }
 
        public void RollbackTransaction()
        {
            if (!ShouldCommit) return;
            _completed = true;
 
            if (IsOpenTransaction(_transaction))
                _transaction.Rollback();
            _transaction = null;
        }
 
        private static bool IsOpenTransaction(ITransaction transaction)
        {
            return transaction != null && !transaction.WasCommitted && !transaction.WasRolledBack;
        }
 
        #endregion
 
 
        #region IDisposable Members
 
        public void Dispose()
        {
            if (!ShouldCommit) return;
            RollbackTransaction();
        }
 
        #endregion
    }
}