v9.7.2018.0421   支持运行时修改DAL连接字符串
大石头 编写于 2018-04-21 14:00:47
X
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using NewLife.Collections;
using NewLife.Log;

namespace XCode.DataAccessLayer
{
    /// <summary>事务对象</summary>
    interface ITransaction
    {
        /// <summary>事务隔离级别</summary>
        IsolationLevel Level { get; }

        /// <summary>事务计数。当且仅当事务计数等于1时,才提交或回滚。</summary>
        Int32 Count { get; }

        /// <summary>执行次数。其决定是否更新缓存</summary>
        Int32 Executes { get; }

        /// <summary>数据库事务</summary>
        DbTransaction Tran { get; }

        /// <summary>事务依赖对象</summary>
        IList<Object> Attachs { get; }

        /// <summary>事务完成事件</summary>
        event EventHandler<TransactionEventArgs> Completed;

        /// <summary>获取事务</summary>
        /// <param name="cmd">命令</param>
        /// <param name="execute">是否执行增删改</param>
        /// <returns></returns>
        DbTransaction Check(DbCommand cmd, Boolean execute);

        /// <summary>增加事务计数</summary>
        /// <returns></returns>
        ITransaction Begin();

        /// <summary>提交事务</summary>
        /// <returns></returns>
        ITransaction Commit();

        /// <summary>回滚事务</summary>
        /// <returns></returns>
        ITransaction Rollback();
    }

    /// <summary>事务完成事件参数</summary>
    class TransactionEventArgs : EventArgs
    {
        /// <summary>事务是否成功</summary>
        public Boolean Success { get; }

        /// <summary>执行次数。其决定是否更新缓存</summary>
        public Int32 Executes { get; }

        public TransactionEventArgs(Boolean success, Int32 executes)
        {
            Success = success;
            Executes = executes;
        }
    }

    class Transaction : ITransaction
    {
        #region 属性
        public IsolationLevel Level { get; private set; }

        public Int32 Count { get; set; }

        /// <summary>执行次数。其决定是否更新缓存</summary>
        public Int32 Executes { get; set; }

        /// <summary>事务依赖对象</summary>
        public IList<Object> Attachs { get; } = new List<Object>();

        public event EventHandler<TransactionEventArgs> Completed;

        private static Int32 _gid;
        /// <summary>事务唯一编号</summary>
        private Int32 ID { get; set; }

        IDbSession _Session;

        /// <summary>连接对象</summary>
        public DbConnection Conn { get; private set; }
        #endregion

        #region 构造
        public Transaction(IDbSession session, IsolationLevel level)
        {
            _Session = session;
            Level = level;
            Count = 1;

            // 打开事务后,由事务管理连接
            Conn = _Session.Database.Pool.Acquire();
        }
        #endregion

        #region 延迟打开事务
        private DbTransaction _Tran;
        /// <summary>数据库事务。首次使用打开事务</summary>
        public DbTransaction Tran { get { return _Tran; } }

        /// <summary>获取事务</summary>
        /// <param name="cmd">命令</param>
        /// <param name="execute">是否执行增删改</param>
        /// <returns></returns>
        public DbTransaction Check(DbCommand cmd, Boolean execute)
        {
            if (cmd.Transaction != null) return cmd.Transaction;

            cmd.Transaction = _Tran;
            if (cmd.Connection == null) cmd.Connection = Conn;

            // 不要为查询打开事务
            if (!execute) return _Tran;

            Executes++;

            if (_Tran != null) return _Tran;

            //var ss = _Session;
            //if (!ss.Opened) ss.Open();

            _Tran = Conn.BeginTransaction(Level);
            cmd.Transaction = _Tran;
            if (cmd.Connection == null) cmd.Connection = Conn;

            Level = _Tran.IsolationLevel;
            ID = ++_gid;
            Log.Debug("Tran.Begin {0} {1}", ID, Level);

            return _Tran;
        }
        #endregion

        #region 方法
        public ITransaction Begin()
        {
            if (Count <= 0) throw new ArgumentOutOfRangeException(nameof(Count), $"事务[{ID}]不能重新开始");

            Count++;

            return this;
        }

        public ITransaction Commit()
        {
            if (Count <= 0) throw new ArgumentOutOfRangeException(nameof(Count), $"事务[{ID}]未开始或已结束");

            Count--;

            if (Count == 0)
            {
                var tr = _Tran;
                try
                {
                    if (tr != null)
                    {
                        Log.Debug("Tran.Commit {0} {1} Executes={2}", ID, Level, Executes);

                        tr.Commit();
                    }
                }
                finally
                {
                    _Tran = null;
                    Completed?.Invoke(this, new TransactionEventArgs(true, Executes));

                    // 把连接归还给对象池
                    _Session.Database.Pool.Release(Conn);
                }
            }

            return this;
        }

        public ITransaction Rollback()
        {
            if (Count <= 0) throw new ArgumentOutOfRangeException(nameof(Count), $"事务[{ID}]未开始或已结束");

            Count--;

            if (Count == 0)
            {
                var tr = _Tran;
                try
                {
                    if (tr != null)
                    {
                        Log.Debug("Tran.Rollback {0} {1} Executes={2}", ID, Level, Executes);

                        tr.Rollback();
                    }
                }
                finally
                {
                    _Tran = null;
                    Completed?.Invoke(this, new TransactionEventArgs(false, Executes));

                    // 把连接归还给对象池
                    _Session.Database.Pool.Release(Conn);
                }
            }

            return this;
        }
        #endregion

        #region 日志
        /// <summary>日志</summary>
        public ILog Log { get; set; } = Logger.Null;
        #endregion
    }
}