zoukankan      html  css  js  c++  java
  • Petapoco 数据库操作类

    Petapoco数据库操作类

    using System;
    using System.Collections.Generic;
    using System.Configuration;
    using System.Data;
    using System.Data.Common;
    using System.Data.SqlClient;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Reflection;
    using System.Reflection.Emit;
    using System.Text;
    using System.Text.RegularExpressions;
    
    namespace Jqw.Data.Petapoco
    {
        [AttributeUsage(AttributeTargets.Class)]
        public class ExplicitColumnsAttribute : Attribute
        {
        }
    
        /// <summary>
        /// 忽略该属性
        /// </summary>
        [AttributeUsage(AttributeTargets.Property)]
        public class IgnoreAttribute : Attribute
        {
        }
    
        [AttributeUsage(AttributeTargets.Property)]
        public class ColumnAttribute : Attribute
        {
            public ColumnAttribute() { }
            public ColumnAttribute(string name) { Name = name; }
            public string Name { get; set; }
        }
    
        [AttributeUsage(AttributeTargets.Property)]
        public class ResultColumnAttribute : ColumnAttribute
        {
            public ResultColumnAttribute() { }
            public ResultColumnAttribute(string name) : base(name) { }
        }
    
        [AttributeUsage(AttributeTargets.Class)]
        public class TableAttribute : Attribute
        {
            public TableAttribute(string tableName)
            {
                Value = tableName;
            }
            public string Value { get; private set; }
        }
    
        [AttributeUsage(AttributeTargets.Class)]
        public class PrimaryKeyAttribute : Attribute
        {
            public PrimaryKeyAttribute(string primaryKey)
            {
                Value = primaryKey;
                AutoIncrement = true;
            }
    
            public string Value { get; private set; }
            public string SequenceName { get; set; }
            public bool AutoIncrement { get; set; }
        }
    
        [AttributeUsage(AttributeTargets.Property)]
        public class AutoJoinAttribute : Attribute
        {
            public AutoJoinAttribute() { }
        }
    
        public class Page<T>
        {
            /// <summary>
            /// 当前页
            /// </summary>
            public long CurrentPage { get; set; }
            /// <summary>
            /// 总分页数
            /// </summary>
            public long TotalPages { get; set; }
            /// <summary>
            /// 总记录数
            /// </summary>
            public long TotalRecords { get; set; }
            /// <summary>
            /// 每页记录数
            /// </summary>
            public long PageSize { get; set; }
            /// <summary>
            /// 记录集
            /// </summary>
            public List<T> Items { get; set; }
        }
    
        public class Page<T, TU>
        {
            public T Model { get; set; }
    
            /// <summary>
            /// 记录集
            /// </summary>
            public Page<TU> ItemList { get; set; }
        }
    
        public class AnsiString
        {
            public AnsiString(string str)
            {
                Value = str;
            }
            public string Value { get; private set; }
        }
    
        public class TableInfo
        {
            public string TableNameBack { get; set; }
            public string TableName { get; set; }
            public string PrimaryKey { get; set; }
            public bool AutoIncrement { get; set; }
            public string SequenceName { get; set; }
        }
    
        public interface IMapper
        {
            void GetTableInfo(Type t, TableInfo ti);
            bool MapPropertyToColumn(PropertyInfo pi, ref string columnName, ref bool resultColumn);
            Func<object, object> GetFromDbConverter(PropertyInfo pi, Type sourceType);
            Func<object, object> GetToDbConverter(Type sourceType);
        }
    
        public interface IMapper2 : IMapper
        {
            Func<object, object> GetFromDbConverter(Type destType, Type sourceType);
        }
    
        public class Database : IDisposable
        {
            private static readonly object ConnectionLock = new object();
    
            /// <summary>
            ///  数据库连接
            /// </summary>
            public class DatabaseType
            {
                //public const string MsSqlRead = "MsSqlRead";
                //public const string MsSqlWrite = "MsSqlWrite";
            }
    
            public Database(IDbConnection connection)
            {
                _sharedConnection = connection;
                _connectionString = connection.ConnectionString;
                _sharedConnectionDepth = 2;        // Prevent closing external connection
                CommonConstruct();
            }
    
            public Database(string connectionString, string providerName)
            {
                _connectionString = connectionString;
                _providerName = providerName;
                CommonConstruct();
            }
    
            public Database(string connectionString, DbProviderFactory provider)
            {
                _connectionString = connectionString;
                _factory = provider;
                CommonConstruct();
            }
    
            public Database(string connectionStringName)
            {
                // Use first?
                if (connectionStringName == "")
                    connectionStringName = ConfigurationManager.ConnectionStrings[0].Name;
    
                // Work out connection string and provider name
                var providerName = "System.Data.SqlClient";
                if (ConfigurationManager.ConnectionStrings[connectionStringName] != null)
                {
                    if (!string.IsNullOrEmpty(ConfigurationManager.ConnectionStrings[connectionStringName].ProviderName))
                        providerName = ConfigurationManager.ConnectionStrings[connectionStringName].ProviderName;
                }
                else
                {
                    throw new InvalidOperationException("Can't find a connection string with the name '" + connectionStringName + "'");
                }
    
                // Store factory and connection string
                _connectionString = ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString;
    
                _providerName = providerName;
                CommonConstruct();
            }
    
            enum DBType
            {
                SqlServer,
                SqlServerCe,
                MySql,
                PostgreSql,
                Oracle,
                SqlLite
            }
            DBType _dbType = DBType.SqlServer;
    
            private void CommonConstruct()
            {
                _transactionDepth = 0;
                EnableAutoSelect = true;
                EnableNamedParams = true;
                ForceDateTimesToUtc = true;
    
                if (_providerName != null)
                    _factory = DbProviderFactories.GetFactory(_providerName);
    
                string dbtype = (_factory == null ? _sharedConnection.GetType() : _factory.GetType()).Name;
    
                // Try using type name first (more reliable)
                if (dbtype.StartsWith("MySql")) _dbType = DBType.MySql;
                else if (dbtype.StartsWith("SqlCe")) _dbType = DBType.SqlServerCe;
                else if (dbtype.StartsWith("Npgsql")) _dbType = DBType.PostgreSql;
                else if (dbtype.StartsWith("Oracle")) _dbType = DBType.Oracle;
                else if (dbtype.StartsWith("SQLite")) _dbType = DBType.SqlLite;
                else if (dbtype.StartsWith("System.Data.SqlClient.")) _dbType = DBType.SqlServer;
                // else try with provider name
                else if (_providerName.IndexOf("MySql", StringComparison.InvariantCultureIgnoreCase) >= 0) _dbType = DBType.MySql;
                else if (_providerName.IndexOf("SqlServerCe", StringComparison.InvariantCultureIgnoreCase) >= 0) _dbType = DBType.SqlServerCe;
                else if (_providerName.IndexOf("Npgsql", StringComparison.InvariantCultureIgnoreCase) >= 0) _dbType = DBType.PostgreSql;
                else if (_providerName.IndexOf("Oracle", StringComparison.InvariantCultureIgnoreCase) >= 0) _dbType = DBType.Oracle;
                else if (_providerName.IndexOf("SQLite", StringComparison.InvariantCultureIgnoreCase) >= 0) _dbType = DBType.SqlLite;
    
                if (_dbType == DBType.MySql && _connectionString != null && _connectionString.IndexOf("Allow User Variables=true") >= 0)
                    _paramPrefix = "?";
                if (_dbType == DBType.Oracle)
                    _paramPrefix = ":";
            }
    
            public void Dispose()
            {
                // Automatically close one open connection reference
                //  (Works with KeepConnectionAlive and manually opening a shared connection)
                CloseSharedConnection();
            }
    
    
            public bool KeepConnectionAlive { set; get; }
    
            public void OpenSharedConnection()
            {
                //if (_sharedConnectionDepth == 0)
                //{
                //    _sharedConnection = _factory.CreateConnection();
                //    if (KeepConnectionAlive)
                //        _sharedConnectionDepth++; // Make sure you call Dispose
                //}
                //if (_sharedConnection != null && _sharedConnection.State != ConnectionState.Open)
                //{
                //    _sharedConnection.ConnectionString = _connectionString;
                //    while (_sharedConnection.State == ConnectionState.Connecting)
                //    {
                //        //Do Nothing until things are connected.
                //    }
                //    if (_sharedConnection.State == ConnectionState.Broken)
                //    {
                //        _sharedConnection.Close();
                //    }
                //    if (_sharedConnection.State != ConnectionState.Open)
                //        _sharedConnection.Open();
                //    _sharedConnection = OnConnectionOpened(_sharedConnection);
                //}
                //_sharedConnectionDepth++;
                lock (ConnectionLock)
                {
                    if (_sharedConnectionDepth == 0)
                    {
                        _sharedConnection = _factory.CreateConnection();
                        if (_sharedConnection != null)
                        {
                            _sharedConnection.ConnectionString = _connectionString;
    
                            if (_sharedConnection.State == ConnectionState.Broken)
                                _sharedConnection.Close();
    
                            if (_sharedConnection.State == ConnectionState.Closed)
                                _sharedConnection.Open();
    
                            _sharedConnection = OnConnectionOpened(_sharedConnection);
                        }
    
                        if (KeepConnectionAlive)
                            _sharedConnectionDepth++; // Make sure you call Dispose
                    }
                    _sharedConnectionDepth++;
                }
            }
    
            public void CloseSharedConnection()
            {
                lock (ConnectionLock)
                {
                    if (_sharedConnectionDepth > 0)
                    {
                        _sharedConnectionDepth--;
                        if (_sharedConnectionDepth == 0 && _sharedConnection != null)
                        {
                            OnConnectionClosing(_sharedConnection);
                            _sharedConnection.Dispose();
                            _sharedConnection = null;
                        }
                    }
                }
            }
    
            public IDbConnection Connection
            {
                get { return _sharedConnection; }
            }
    
            public Transaction GetTransaction()
            {
                return new Transaction(this);
            }
    
            public virtual void OnBeginTransaction() { }
            public virtual void OnEndTransaction() { }
    
            /// <summary>
            /// 开始事务
            /// </summary>
            public void BeginTransaction()
            {
                _transactionDepth++;
                if (_transactionDepth == 1)
                {
                    OpenSharedConnection();
                    _transaction = _sharedConnection.BeginTransaction();
                    _transactionCancelled = false;
                    OnBeginTransaction();
                }
            }
    
    
            void CleanupTransaction()
            {
                OnEndTransaction();
    
                if (_transactionCancelled)
                    _transaction.Rollback();
                else
                    _transaction.Commit();
    
                _transaction.Dispose();
                _transaction = null;
                CloseSharedConnection();
            }
    
            /// <summary>
            /// 取消回滚事务
            /// </summary>
            public void AbortTransaction()
            {
                _transactionCancelled = true;
                if ((--_transactionDepth) == 0)
                    CleanupTransaction();
            }
    
            /// <summary>
            /// 提交事务
            /// </summary>
            public void CompleteTransaction()
            {
                if ((--_transactionDepth) == 0)
                    CleanupTransaction();
            }
    
            static Regex rxParams = new Regex(@"(?<!@)@w+", RegexOptions.Compiled);
            public static string ProcessParams(string _sql, object[] args_src, List<object> args_dest)
            {
                return rxParams.Replace(_sql, m =>
                {
                    string param = m.Value.Substring(1);
    
                    object arg_val;
                    bool needparam = true;
                    int paramIndex;
                    if (int.TryParse(param, out paramIndex))
                    {
                        // Numbered parameter
                        if (paramIndex < 0 || paramIndex >= args_src.Length)
                            throw new ArgumentOutOfRangeException(string.Format("Parameter '@{0}' specified but only {1} parameters supplied (in `{2}`)", paramIndex, args_src.Length, _sql));
                        arg_val = args_src[paramIndex];
                    }
                    else
                    {
                        // Look for a property on one of the arguments with this name
                        bool found = false;
                        arg_val = null;
                        if (args_src != null)
                        {
                            foreach (var o in args_src)
                            {
                                var pi = o.GetType().GetProperty(param);
                                if (pi != null)
                                {
                                    arg_val = pi.GetValue(o, null);
                                    found = true;
                                    break;
                                }
                            }
                        }
                        if (!found) needparam = false;
                        //    throw new ArgumentException(string.Format("Parameter '@{0}' specified but none of the passed arguments have a property with this name (in '{1}')", param, _sql));
                    }
    
                    // Expand collections to parameter lists
                    if ((arg_val as System.Collections.IEnumerable) != null &&
                        (arg_val as string) == null &&
                        (arg_val as byte[]) == null)
                    {
                        var sb = new StringBuilder();
                        foreach (var i in arg_val as System.Collections.IEnumerable)
                        {
                            sb.Append((sb.Length == 0 ? "@" : ",@") + args_dest.Count.ToString());
                            args_dest.Add(i);
                        }
                        return sb.ToString();
                    }
                    else
                    {
                        if (needparam)
                        {
                            args_dest.Add(arg_val);
                            return "@" + (args_dest.Count - 1).ToString();
                        }
                        return String.Format("@{0}", param);
                    }
                }
                );
            }
    
            void AddParam(IDbCommand cmd, object item, string ParameterPrefix)
            {
                // Convert value to from poco type to db type
                if (Database.Mapper != null && item != null)
                {
                    var fn = Database.Mapper.GetToDbConverter(item.GetType());
                    if (fn != null)
                        item = fn(item);
                }
    
                // Support passed in parameters
                var idbParam = item as IDbDataParameter;
                if (idbParam != null)
                {
                    idbParam.ParameterName = string.Format("{0}{1}", ParameterPrefix, cmd.Parameters.Count);
                    cmd.Parameters.Add(idbParam);
                    return;
                }
    
                var p = cmd.CreateParameter();
                p.ParameterName = string.Format("{0}{1}", ParameterPrefix, cmd.Parameters.Count);
                if (item == null)
                {
                    p.Value = "";
                }
                else
                {
                    var t = item.GetType();
                    if (t.IsEnum)        // PostgreSQL .NET driver wont cast enum to int
                    {
                        p.Value = (int)item;
                    }
                    else if (t == typeof(DateTime))
                    {
                        if (((DateTime)item).Year == 1)
                        {
                            p.Value = DateTime.Parse("1900-1-1");
                        }
                        else
                        {
                            p.Value = item;
                        }
                    }
                    else if (t == typeof(Guid))
                    {
                        p.Value = item.ToString();
                        p.DbType = DbType.String;
                        p.Size = 40;
                    }
                    else if (t == typeof(string))
                    {
                        p.Size = Math.Max((item as string).Length + 1, 4000);        // Help query plan caching by using common size
                        p.Value = item;
                    }
                    else if (t == typeof(AnsiString))
                    {
                        // Thanks @DataChomp for pointing out the SQL Server indexing performance hit of using wrong string type on varchar
                        p.Size = Math.Max((item as AnsiString).Value.Length + 1, 4000);
                        p.Value = (item as AnsiString).Value;
                        p.DbType = DbType.AnsiString;
                    }
    
                    else if (item.GetType().Name == "SqlGeography") //SqlGeography is a CLR Type
                    {
                        p.GetType().GetProperty("UdtTypeName").SetValue(p, "geography", null); //geography is the equivalent SQL Server Type
                        p.Value = item;
                    }
    
                    else if (item.GetType().Name == "SqlGeometry") //SqlGeometry is a CLR Type
                    {
                        p.GetType().GetProperty("UdtTypeName").SetValue(p, "geometry", null); //geography is the equivalent SQL Server Type
                        p.Value = item;
                    }
                    else
                    {
                        p.Value = item;
                    }
                }
    
                cmd.Parameters.Add(p);
            }
    
            static Regex rxParamsPrefix = new Regex(@"(?<!@)@w+", RegexOptions.Compiled);
            public IDbCommand CreateCommand(IDbConnection connection, string sql, params object[] args)
            {
                if (EnableNamedParams)
                {
                    var new_args = new List<object>();
                    sql = ProcessParams(sql, args, new_args);
                    args = new_args.ToArray();
                }
                if (_paramPrefix != "@")
                    sql = rxParamsPrefix.Replace(sql, m => _paramPrefix + m.Value.Substring(1));
                sql = sql.Replace("@@", "@");           // <- double @@ escapes a single @
    
                IDbCommand cmd = connection.CreateCommand();
                cmd.Connection = connection;
                sql = AppendComment(sql);
                cmd.CommandText = sql;
                cmd.Transaction = _transaction;
                foreach (var item in args)
                {
                    AddParam(cmd, item, _paramPrefix);
                }
                if (!String.IsNullOrEmpty(sql))
                    DoPreExecute(cmd);
                return cmd;
            }
    
            public virtual void OnException(Exception x)
            {
                System.Diagnostics.Debug.WriteLine(x.ToString());
                System.Diagnostics.Debug.WriteLine(LastCommand);
            }
    
            public virtual IDbConnection OnConnectionOpened(IDbConnection conn)
            {
                return conn;
            }
            public virtual void OnConnectionClosing(IDbConnection conn)
            {
            }
            public virtual void OnExecutingCommand(IDbCommand cmd)
            {
    
            }
            public virtual void OnExecutedCommand(IDbCommand cmd)
            {
    
            }
    
            public int Execute(string sql, params object[] args)
            {
                try
                {
                    OpenSharedConnection();
                    try
                    {
                        using (var cmd = CreateCommand(_sharedConnection, sql, args))
                        {
                            var retv = cmd.ExecuteNonQuery();
                            OnExecutedCommand(cmd);
                            return retv;
                        }
                    }
                    finally
                    {
                        CloseSharedConnection();
                    }
                }
                catch (Exception x)
                {
                    OnException(x);
                    //throw;
                    return 0;
                }
    
            }
    
            public int Execute(Sql sql)
            {
                return Execute(sql.SQL, sql.Arguments);
            }
    
            /// <summary>
            /// </summary>
            /// <param name="sql"></param>
            /// <param name="args"></param>
            /// <returns></returns>
            public object ExecuteScalar(string sql, params object[] args)
            {
                try
                {
                    OpenSharedConnection();
                    try
                    {
                        using (var cmd = CreateCommand(_sharedConnection, sql, args))
                        {
                            object val = cmd.ExecuteScalar();
                            OnExecutedCommand(cmd);
                            return val;
                        }
                    }
                    finally
                    {
                        CloseSharedConnection();
                    }
                }
                catch (Exception x)
                {
                    OnException(x);
                    //throw;
                    return null;
                }
            }
    
            /// <summary>
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="sql"></param>
            /// <param name="args"></param>
            /// <returns></returns>
            public T ExecuteScalar<T>(string sql, params object[] args)
            {
                try
                {
                    var obj = ExecuteScalar(sql, args);
                    return (T)Convert.ChangeType(obj, typeof(T));
                }
                catch (Exception x)
                {
                    OnException(x);
                    //throw;
                    return default(T);
                }
            }
    
            public T ExecuteScalar<T>(Sql sql)
            {
                return ExecuteScalar<T>(sql.SQL, sql.Arguments);
            }
    
            Regex rxSelect = new Regex(@"As*(SELECT|EXECUTE|CALL|EXEC)s", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Multiline);
            Regex rxFrom = new Regex(@"As*FROMs", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Multiline);
            string AddSelectClause<T>(string sql, params Expression<Func<T, object>>[] expressions)
            {
                if (sql.StartsWith(";"))
                    return sql.Substring(1);
                if (!rxSelect.IsMatch(sql))
                {
                    var pd = PocoData.ForType(typeof(T), TablePerfixFunc, TablePerfix);
                    var tableName = EscapeTableName(pd.TableInfo.TableName);
                    string cols = (expressions == null || expressions.Length.Equals(0)) ? string.Join(", ", (from c in pd.QueryColumns select tableName + "." + EscapeSqlIdentifier(c)).ToArray()) : string.Join(", ", (from c in expressions select tableName + "." + GetTableColumn(c)).ToArray());
                    if (!rxFrom.IsMatch(sql))
                        sql = string.Format("SELECT {0} FROM {1} with(nolock) {2}", cols, tableName, sql);
                    else
                        sql = string.Format("SELECT {0} with(nolock) {1}", cols, sql);
                }
                return sql;
            }
            string AddSelectClause<T>(string sql, bool isIgnoreResultColumn = true, params Expression<Func<T, object>>[] expressions)
            {
                if (sql.StartsWith(";"))
                    return sql.Substring(1);
                if (!rxSelect.IsMatch(sql))
                {
                    var pd = PocoData.ForType(typeof(T), TablePerfixFunc, TablePerfix, isIgnoreResultColumn);
                    var tableName = EscapeTableName(pd.TableInfo.TableName);
                    string cols = (expressions == null || expressions.Length.Equals(0)) ? string.Join(", ", (from c in pd.QueryColumns select tableName + "." + EscapeSqlIdentifier(c)).ToArray()) : string.Join(", ", (from c in expressions select tableName + "." + GetTableColumn(c)).ToArray());
                    if (!rxFrom.IsMatch(sql))
                        sql = string.Format("SELECT {0} FROM {1} with(nolock) {2}", cols, tableName, sql);
                    else
                        sql = string.Format("SELECT {0} with(nolock) {1}", cols, sql);
                }
                return sql;
            }
            public bool EnableAutoSelect { get; set; }
            public bool EnableNamedParams { get; set; }
            public bool ForceDateTimesToUtc { get; set; }
    
            public List<T> Fetch<T>(string sql, params object[] args)
            {
                return Query<T>(sql, args).ToList();
            }
    
            public List<T> Fetch<T>(Sql sql)
            {
                return Fetch<T>(sql.SQL, sql.Arguments);
            }
    
            static Regex rxColumns = new Regex(@"As*SELECTs+((?:((?>((?<depth>)|)(?<-depth>)|.?)*(?(depth)(?!)))|.)*?)(?<!,s+)FROM", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.Compiled);
            static Regex rxOrderBy = new Regex(
                    @"ORDERs+BYs+(?!.*?(?:)|s+)ASs)(?:((?>((?<depth>)|)(?<-depth>)|.?)*(?(depth)(?!)))|[[]`""w().])+(?:s+(?:ASC|DESC))?(?:s*,s*(?:((?>((?<depth>)|)(?<-depth>)|.?)*(?(depth)(?!)))|[[]`""w().])+(?:s+(?:ASC|DESC))?)*",
                    RegexOptions.RightToLeft | RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.Compiled);
    
            //static Regex rxOrderBy = new Regex(@"ORDERs+BYs+(?:((?>((?<depth>)|)(?<-depth>)|.?)*(?(depth)(?!)))|[w().])+(?:s+(?:ASC|DESC))?(?:s*,s*(?:((?>((?<depth>)|)(?<-depth>)|.?)*(?(depth)(?!)))|[w().])+(?:s+(?:ASC|DESC))?)*", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.Compiled);
            static Regex rxDistinct = new Regex(@"ADISTINCTs", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.Compiled);
            public static bool SplitSqlForPaging(string sql, out string sqlCount, out string sqlSelectRemoved, out string sqlOrderBy)
            {
                sqlSelectRemoved = null;
                sqlCount = null;
                sqlOrderBy = null;
    
                // Extract the columns from "SELECT <whatever> FROM"
                var m = rxColumns.Match(sql);
                if (!m.Success)
                    return false;
    
                // Save column list and replace with COUNT(*)
                Group g = m.Groups[1];
                sqlSelectRemoved = sql.Substring(g.Index);
    
                if (rxDistinct.IsMatch(sqlSelectRemoved))
                    sqlCount = sql.Substring(0, g.Index) + "COUNT(" + m.Groups[1].ToString().Trim() + ") " + sql.Substring(g.Index + g.Length);
                else
                    sqlCount = sql.Substring(0, g.Index) + "COUNT(1) " + sql.Substring(g.Index + g.Length);
    
    
                // Look for an "ORDER BY <whatever>" clause
                m = rxOrderBy.Match(sqlCount);
                if (!m.Success)
                {
                    sqlOrderBy = null;
                }
                else
                {
                    g = m.Groups[0];
                    sqlOrderBy = g.ToString();
                    sqlCount = sqlCount.Substring(0, g.Index) + sqlCount.Substring(g.Index + g.Length);
                }
    
                return true;
            }
    
            public void BuildPageQueries<T>(long skip, long take, string sql, ref object[] args, out string sqlCount, out string sqlPage)
            {
                // Add auto select clause
                if (EnableAutoSelect)
                    sql = AddSelectClause<T>(sql);
    
                // Split the SQL into the bits we need
                string sqlSelectRemoved, sqlOrderBy;
                if (!SplitSqlForPaging(sql, out sqlCount, out sqlSelectRemoved, out sqlOrderBy))
                    throw new Exception("Unable to parse SQL statement for paged query");
                sqlSelectRemoved = rxOrderBy.Replace(sqlSelectRemoved, "");
                if (rxDistinct.IsMatch(sqlSelectRemoved))
                {
                    sqlSelectRemoved = "peta_inner.* FROM (SELECT " + sqlSelectRemoved + ") peta_inner";
                }
                sqlPage = string.Format("SELECT * FROM (SELECT ROW_NUMBER() OVER ({0}) peta_rn, {1}) peta_paged WHERE peta_rn>@{2} AND peta_rn<=@{3}",
                                        sqlOrderBy == null ? "ORDER BY (SELECT NULL)" : sqlOrderBy, sqlSelectRemoved, args.Length, args.Length + 1);
                args = args.Concat(new object[] { skip, skip + take }).ToArray();
            }
    
            public void BuildPageQueries<T>(long skip, long take, string sql, ref object[] args, out string sqlCount, out string sqlPage, bool isIgnoreResultColumn = true)
            {
                // Add auto select clause
                if (EnableAutoSelect)
                    sql = AddSelectClause<T>(sql, isIgnoreResultColumn);
    
                // Split the SQL into the bits we need
                string sqlSelectRemoved, sqlOrderBy;
                if (!SplitSqlForPaging(sql, out sqlCount, out sqlSelectRemoved, out sqlOrderBy))
                    throw new Exception("Unable to parse SQL statement for paged query");
                //update by jqw2180424
                if (sqlSelectRemoved.Contains(sqlOrderBy))
                {
                    try
                    {
                        //int count = Regex.Matches(sqlSelectRemoved, sqlOrderBy).Count;
                        int indexof = sqlSelectRemoved.LastIndexOf(sqlOrderBy, StringComparison.Ordinal);
                        if (indexof < sqlSelectRemoved.Length)
                        {
                            string beforesql = sqlSelectRemoved.Substring(0, indexof);
                            if ((indexof + sqlOrderBy.Length) < sqlSelectRemoved.Length)
                            {
                                string aftersql = sqlSelectRemoved.Substring(indexof + sqlOrderBy.Length, sqlSelectRemoved.Length - indexof - sqlOrderBy.Length);
                                sqlSelectRemoved = beforesql + aftersql;
                            }
                            else
                            {
                                sqlSelectRemoved = beforesql;
                            }
                        }
                    }
                    catch (Exception)
                    {
    
                    }
                }
                //sqlSelectRemoved = rxOrderBy.Replace(sqlSelectRemoved, "");
                if (rxDistinct.IsMatch(sqlSelectRemoved))
                {
                    sqlSelectRemoved = "peta_inner.* FROM (SELECT " + sqlSelectRemoved + ") peta_inner";
                }
                sqlPage = string.Format("SELECT * FROM (SELECT ROW_NUMBER() OVER ({0}) peta_rn, {1}) peta_paged WHERE peta_rn>@{2} AND peta_rn<=@{3}",
                                        sqlOrderBy == null ? "ORDER BY (SELECT NULL)" : sqlOrderBy, sqlSelectRemoved, args.Length, args.Length + 1);
                args = args.Concat(new object[] { skip, skip + take }).ToArray();
            }
    
            public void BuildPageQueries<T>(long skip, long take, string sql, Expression<Func<T, object>>[] expressions, ref object[] args, out string sqlCount, out string sqlPage)
            {
                // Add auto select clause
                if (EnableAutoSelect)
                    sql = AddSelectClause<T>(sql, expressions);
    
                // Split the SQL into the bits we need
                string sqlSelectRemoved, sqlOrderBy;
                if (!SplitSqlForPaging(sql, out sqlCount, out sqlSelectRemoved, out sqlOrderBy))
                    throw new Exception("Unable to parse SQL statement for paged query");
                sqlSelectRemoved = rxOrderBy.Replace(sqlSelectRemoved, "");
                if (rxDistinct.IsMatch(sqlSelectRemoved))
                {
                    sqlSelectRemoved = "peta_inner.* FROM (SELECT " + sqlSelectRemoved + ") peta_inner";
                }
                sqlPage = string.Format("SELECT * FROM (SELECT ROW_NUMBER() OVER ({0}) peta_rn, {1}) peta_paged WHERE peta_rn>@{2} AND peta_rn<=@{3}",
                                        sqlOrderBy == null ? "ORDER BY (SELECT NULL)" : sqlOrderBy, sqlSelectRemoved, args.Length, args.Length + 1);
                args = args.Concat(new object[] { skip, skip + take }).ToArray();
            }
    
            public Page<T> Page<T>(long page, long itemsPerPage, string sql = "", params object[] args)
            {
                string sqlCount, sqlPage;
                BuildPageQueries<T>((page - 1) * itemsPerPage, itemsPerPage, sql, ref args, out sqlCount, out sqlPage);
    
                // Save the one-time command time out and use it for both queries
                int saveTimeout = OneTimeCommandTimeout;
    
                // Setup the paged result
                var result = new Page<T>();
                result.CurrentPage = page;
                result.PageSize = itemsPerPage;
                result.TotalRecords = ExecuteScalar<long>(sqlCount, args);
                result.TotalPages = result.TotalRecords / itemsPerPage;
                if ((result.TotalRecords % itemsPerPage) != 0)
                    result.TotalPages++;
    
                OneTimeCommandTimeout = saveTimeout;
    
                // Get the records
                result.Items = Fetch<T>(sqlPage, args);
    
                // Done
                return result;
            }
    
            public Page<T> Page<T>(long page, long itemsPerPage, string sql = "", Expression<Func<T, object>>[] expressions = null, params object[] args)
            {
                string sqlCount, sqlPage;
                if (expressions == null || expressions.Length.Equals(0))
                {
                    BuildPageQueries<T>((page - 1) * itemsPerPage, itemsPerPage, sql, ref args, out sqlCount, out sqlPage);
                }
                else
                {
                    BuildPageQueries<T>((page - 1) * itemsPerPage, itemsPerPage, sql, expressions, ref args, out sqlCount,
                                        out sqlPage);
                }
                // Save the one-time command time out and use it for both queries
                int saveTimeout = OneTimeCommandTimeout;
    
                // Setup the paged result
                var result = new Page<T>();
                result.CurrentPage = page;
                result.PageSize = itemsPerPage;
                result.TotalRecords = ExecuteScalar<long>(sqlCount, args);
                result.TotalPages = result.TotalRecords / itemsPerPage;
                if ((result.TotalRecords % itemsPerPage) != 0)
                    result.TotalPages++;
    
                OneTimeCommandTimeout = saveTimeout;
    
                // Get the records
                result.Items = Fetch<T>(sqlPage, args);
    
                // Done
                return result;
            }
    
            //add by jqw 20160721
            public Page<T> Page<T>(bool isIgnoreResultColumn = true, long page = 1, long itemsPerPage = 10, string sql = "", params object[] args)
            {
                string sqlCount, sqlPage;
                BuildPageQueries<T>((page - 1) * itemsPerPage, itemsPerPage, sql, ref args, out sqlCount, out sqlPage, isIgnoreResultColumn);
    
                // Save the one-time command time out and use it for both queries
                int saveTimeout = OneTimeCommandTimeout;
    
                // Setup the paged result
                var result = new Page<T>();
                result.CurrentPage = page;
                result.PageSize = itemsPerPage;
                result.TotalRecords = ExecuteScalar<long>(sqlCount, args);
                result.TotalPages = result.TotalRecords / itemsPerPage;
                if ((result.TotalRecords % itemsPerPage) != 0)
                    result.TotalPages++;
    
                OneTimeCommandTimeout = saveTimeout;
    
                // Get the records
                result.Items = Fetch<T>(sqlPage, isIgnoreResultColumn, args);
    
                // Done
                return result;
            }
    
            //add by jqw 20160721
            public List<T> Fetch<T>(string sql, bool isIgnoreResultColumn = true, params object[] args)
            {
                return Query<T>(isIgnoreResultColumn, sql, args).ToList();
            }
    
            public Page<T> Page<T>(long page, long itemsPerPage, Sql sql)
            {
                return Page<T>(page, itemsPerPage, sql.SQL, sql.Arguments);
            }
    
            public List<T> Fetch<T>(long page, long itemsPerPage, string sql = "", params object[] args)
            {
                return SkipTake<T>((page - 1) * itemsPerPage, itemsPerPage, sql, args);
            }
    
            public List<T> Fetch<T>(long page, long itemsPerPage, Sql sql)
            {
                return SkipTake<T>((page - 1) * itemsPerPage, itemsPerPage, sql.SQL, sql.Arguments);
            }
    
            public List<T> SkipTake<T>(long skip, long take, string sql = "", params object[] args)
            {
                string sqlCount, sqlPage;
                BuildPageQueries<T>(skip, take, sql, ref args, out sqlCount, out sqlPage);
                return Fetch<T>(sqlPage, args);
            }
    
            public List<T> SkipTake<T>(long skip, long take, Sql sql)
            {
                return SkipTake<T>(skip, take, sql.SQL, sql.Arguments);
            }
    
            /// <summary>
            /// 查询(返回DataTable,可用于多表查询)
            /// </summary>
            /// <param name="sql">SQL Script</param>
            /// <param name="args">参数集</param>
            /// <returns></returns>
            public DataTable Query(string sql, params object[] args)
            {
                DataTable dt = new DataTable();
                OpenSharedConnection();
                try
                {
                    using (var cmd = CreateCommand(_sharedConnection, sql, args))
                    {
                        using (DbDataAdapter dbDataAdapter = _factory.CreateDataAdapter())
                        {
                            if (dbDataAdapter != null)
                            {
                                dbDataAdapter.SelectCommand = (DbCommand)cmd;
                                dbDataAdapter.Fill(dt);
                            }
                        }
                    }
                }
                catch (Exception x)
                {
                    OnException(x);
                }
                finally
                {
                    CloseSharedConnection();
                }
                return dt;
            }
    
            /// <summary>
            /// 查询(返回DataSet,可用于多表查询)
            /// </summary>
            /// <param name="sql">SQL Script</param>
            /// <param name="args">参数集</param>
            /// <returns></returns>
            public DataSet QueryExtend(string sql = "", params object[] args)
            {
                DataSet ds = new DataSet();
                OpenSharedConnection();
                try
                {
                    using (var cmd = CreateCommand(_sharedConnection, sql, args))
                    {
                        using (DbDataAdapter dbDataAdapter = _factory.CreateDataAdapter())
                        {
                            if (dbDataAdapter != null)
                            {
                                dbDataAdapter.SelectCommand = (DbCommand)cmd;
                                dbDataAdapter.Fill(ds);
                            }
                        }
                    }
                }
                catch (Exception x)
                {
                    OnException(x);
                }
                finally
                {
                    CloseSharedConnection();
                }
                return ds;
            }
    
            public IEnumerable<T> Query<T>(string sql = "", params object[] args)
            {
                var isIgnoreResultColumn = true;
                if (EnableAutoSelect)
                    sql = AddSelectClause<T>(sql, isIgnoreResultColumn);
                OpenSharedConnection();
                try
                {
                    using (var cmd = CreateCommand(_sharedConnection, sql, args))
                    {
                        using (IDataReader r = cmd.ExecuteReader())
                        {
                            try
                            {
                                OnExecutedCommand(cmd);
                            }
                            catch (Exception x)
                            {
                                OnException(x);
                                throw;
                            }
                            var pd = PocoData.ForType(typeof(T), TablePerfixFunc, TablePerfix, isIgnoreResultColumn);
                            var factory =
                                pd.GetFactory(cmd.CommandText, _sharedConnection.ConnectionString, ForceDateTimesToUtc, 0,
                                              r.FieldCount, r) as Func<IDataReader, T>;
                            while (true)
                            {
                                T poco;
                                try
                                {
                                    if (!r.Read())
                                    {
                                        yield break;
                                    }
                                    poco = factory(r);
                                }
                                catch (Exception x)
                                {
                                    OnException(x);
                                    throw;
                                }
                                yield return poco;
                            }
                        }
                    }
                }
                finally
                {
                    CloseSharedConnection();
                }
            }
    
            //添加参数isIgnoreResultColumn 2015-07-05
            public IEnumerable<T> Query<T>(bool isIgnoreResultColumn = true, string sql = "", params object[] args)
            {
                if (EnableAutoSelect)
                    sql = AddSelectClause<T>(sql, isIgnoreResultColumn);
                OpenSharedConnection();
                try
                {
                    using (var cmd = CreateCommand(_sharedConnection, sql, args))
                    {
                        using (IDataReader r = cmd.ExecuteReader())
                        {
                            try
                            {
                                OnExecutedCommand(cmd);
                            }
                            catch (Exception x)
                            {
                                OnException(x);
                                throw;
                            }
                            var pd = PocoData.ForType(typeof(T), TablePerfixFunc, TablePerfix, isIgnoreResultColumn);
                            var factory =
                                pd.GetFactory(cmd.CommandText, _sharedConnection.ConnectionString, ForceDateTimesToUtc, 0,
                                              r.FieldCount, r) as Func<IDataReader, T>;
                            while (true)
                            {
                                T poco;
                                try
                                {
                                    if (!r.Read())
                                    {
                                        yield break;
                                    }
                                    poco = factory(r);
                                }
                                catch (Exception x)
                                {
                                    OnException(x);
                                    throw;
                                }
                                yield return poco;
                            }
                        }
                    }
                }
                finally
                {
                    CloseSharedConnection();
                }
            }
    
            private static object HackType(object value, Type conversionType)
            {
                if (conversionType.IsGenericType && conversionType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
                {
                    if (value == null)
                        return null;
    
                    System.ComponentModel.NullableConverter nullableConverter = new System.ComponentModel.NullableConverter(conversionType);
                    conversionType = nullableConverter.UnderlyingType;
                }
                return Convert.ChangeType(value, conversionType);
            }
    
            private static bool IsNullOrDBNull(object obj)
            {
                return obj is DBNull;
            }
    
    
            public List<TRet> Fetch<T1, T2, TRet>(Func<T1, T2, TRet> cb, string sql, params object[] args) { return Query<T1, T2, TRet>(cb, sql, args).ToList(); }
            public List<TRet> Fetch<T1, T2, T3, TRet>(Func<T1, T2, T3, TRet> cb, string sql, params object[] args) { return Query<T1, T2, T3, TRet>(cb, sql, args).ToList(); }
            public List<TRet> Fetch<T1, T2, T3, T4, TRet>(Func<T1, T2, T3, T4, TRet> cb, string sql, params object[] args) { return Query<T1, T2, T3, T4, TRet>(cb, sql, args).ToList(); }
    
    
            public IEnumerable<TRet> Query<T1, T2, TRet>(Func<T1, T2, TRet> cb, string sql, params object[] args) { return Query<TRet>(new Type[] { typeof(T1), typeof(T2) }, cb, sql, args); }
            public IEnumerable<TRet> Query<T1, T2, T3, TRet>(Func<T1, T2, T3, TRet> cb, string sql, params object[] args) { return Query<TRet>(new Type[] { typeof(T1), typeof(T2), typeof(T3) }, cb, sql, args); }
            public IEnumerable<TRet> Query<T1, T2, T3, T4, TRet>(Func<T1, T2, T3, T4, TRet> cb, string sql, params object[] args) { return Query<TRet>(new Type[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4) }, cb, sql, args); }
    
            public List<TRet> Fetch<T1, T2, TRet>(Func<T1, T2, TRet> cb, Sql sql) { return Query<T1, T2, TRet>(cb, sql.SQL, sql.Arguments).ToList(); }
            public List<TRet> Fetch<T1, T2, T3, TRet>(Func<T1, T2, T3, TRet> cb, Sql sql) { return Query<T1, T2, T3, TRet>(cb, sql.SQL, sql.Arguments).ToList(); }
            public List<TRet> Fetch<T1, T2, T3, T4, TRet>(Func<T1, T2, T3, T4, TRet> cb, Sql sql) { return Query<T1, T2, T3, T4, TRet>(cb, sql.SQL, sql.Arguments).ToList(); }
    
            public IEnumerable<TRet> Query<T1, T2, TRet>(Func<T1, T2, TRet> cb, Sql sql) { return Query<TRet>(new Type[] { typeof(T1), typeof(T2) }, cb, sql.SQL, sql.Arguments); }
            public IEnumerable<TRet> Query<T1, T2, T3, TRet>(Func<T1, T2, T3, TRet> cb, Sql sql) { return Query<TRet>(new Type[] { typeof(T1), typeof(T2), typeof(T3) }, cb, sql.SQL, sql.Arguments); }
            public IEnumerable<TRet> Query<T1, T2, T3, T4, TRet>(Func<T1, T2, T3, T4, TRet> cb, Sql sql) { return Query<TRet>(new Type[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4) }, cb, sql.SQL, sql.Arguments); }
    
            public List<T1> Fetch<T1, T2>(string sql, params object[] args) { return Query<T1, T2>(sql, args).ToList(); }
            public List<T1> Fetch<T1, T2, T3>(string sql, params object[] args) { return Query<T1, T2, T3>(sql, args).ToList(); }
            public List<T1> Fetch<T1, T2, T3, T4>(string sql, params object[] args) { return Query<T1, T2, T3, T4>(sql, args).ToList(); }
    
            public IEnumerable<T1> Query<T1, T2>(string sql, params object[] args) { return Query<T1>(new Type[] { typeof(T1), typeof(T2) }, null, sql, args); }
            public IEnumerable<T1> Query<T1, T2, T3>(string sql, params object[] args) { return Query<T1>(new Type[] { typeof(T1), typeof(T2), typeof(T3) }, null, sql, args); }
            public IEnumerable<T1> Query<T1, T2, T3, T4>(string sql, params object[] args) { return Query<T1>(new Type[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4) }, null, sql, args); }
    
            public List<T1> Fetch<T1, T2>(Sql sql) { return Query<T1, T2>(sql.SQL, sql.Arguments).ToList(); }
            public List<T1> Fetch<T1, T2, T3>(Sql sql) { return Query<T1, T2, T3>(sql.SQL, sql.Arguments).ToList(); }
            public List<T1> Fetch<T1, T2, T3, T4>(Sql sql) { return Query<T1, T2, T3, T4>(sql.SQL, sql.Arguments).ToList(); }
    
            public IEnumerable<T1> Query<T1, T2>(Sql sql) { return Query<T1>(new Type[] { typeof(T1), typeof(T2) }, null, sql.SQL, sql.Arguments); }
            public IEnumerable<T1> Query<T1, T2, T3>(Sql sql) { return Query<T1>(new Type[] { typeof(T1), typeof(T2), typeof(T3) }, null, sql.SQL, sql.Arguments); }
            public IEnumerable<T1> Query<T1, T2, T3, T4>(Sql sql) { return Query<T1>(new Type[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4) }, null, sql.SQL, sql.Arguments); }
    
            object GetAutoMapper(Type[] types)
            {
                // Build a key
                var kb = new StringBuilder();
                foreach (var t in types)
                {
                    kb.Append(t.ToString());
                    kb.Append(":");
                }
                var key = kb.ToString();
    
                // Check cache
                RWLock.EnterReadLock();
                try
                {
                    object mapper;
                    if (AutoMappers.TryGetValue(key, out mapper))
                        return mapper;
                }
                finally
                {
                    RWLock.ExitReadLock();
                }
    
                // Create it
                RWLock.EnterWriteLock();
                try
                {
                    // Try again
                    object mapper;
                    if (AutoMappers.TryGetValue(key, out mapper))
                        return mapper;
    
                    // Create a method
                    var m = new DynamicMethod("petapoco_automapper", types[0], types, true);
                    var il = m.GetILGenerator();
    
                    for (int i = 1; i < types.Length; i++)
                    {
                        bool handled = false;
                        for (int j = i - 1; j >= 0; j--)
                        {
                            // Find the property
                            var candidates = from p in types[j].GetProperties() where p.PropertyType == types[i] select p;
                            if (candidates.Count() == 0)
                                continue;
                            if (candidates.Count() > 1)
                                throw new InvalidOperationException(string.Format("Can't auto join {0} as {1} has more than one property of type {0}", types[i], types[j]));
    
                            // Generate code
                            il.Emit(OpCodes.Ldarg_S, j);
                            il.Emit(OpCodes.Ldarg_S, i);
                            il.Emit(OpCodes.Callvirt, candidates.First().GetSetMethod(true));
                            handled = true;
                        }
    
                        if (!handled)
                            throw new InvalidOperationException(string.Format("Can't auto join {0}", types[i]));
                    }
    
                    il.Emit(OpCodes.Ldarg_0);
                    il.Emit(OpCodes.Ret);
    
                    // Cache it
                    var del = m.CreateDelegate(Expression.GetFuncType(types.Concat(types.Take(1)).ToArray()));
                    AutoMappers.Add(key, del);
                    return del;
                }
                finally
                {
                    RWLock.ExitWriteLock();
                }
            }
    
            Delegate FindSplitPoint(Type typeThis, Type typeNext, string sql, IDataReader r, ref int pos)
            {
                // Last?
                if (typeNext == null)
                    return PocoData.ForType(typeThis, TablePerfixFunc, TablePerfix).GetFactory(sql, _sharedConnection.ConnectionString, ForceDateTimesToUtc, pos, r.FieldCount - pos, r);
    
                // Get PocoData for the two types
                PocoData pdThis = PocoData.ForType(typeThis, TablePerfixFunc, TablePerfix);
                PocoData pdNext = PocoData.ForType(typeNext, TablePerfixFunc, TablePerfix);
    
                // Find split point
                int firstColumn = pos;
                var usedColumns = new Dictionary<string, bool>();
                for (; pos < r.FieldCount; pos++)
                {
                    // Split if field name has already been used, or if the field doesn't exist in current poco but does in the next
                    string fieldName = r.GetName(pos);
                    if (usedColumns.ContainsKey(fieldName) || (!pdThis.Columns.ContainsKey(fieldName) && pdNext.Columns.ContainsKey(fieldName)))
                    {
                        return pdThis.GetFactory(sql, _sharedConnection.ConnectionString, ForceDateTimesToUtc, firstColumn, pos - firstColumn, r);
                    }
                    usedColumns.Add(fieldName, true);
                }
    
                throw new InvalidOperationException(string.Format("Couldn't find split point between {0} and {1}", typeThis, typeNext));
            }
    
            class MultiPocoFactory
            {
                public List<Delegate> m_Delegates;
                public Delegate GetItem(int index) { return m_Delegates[index]; }
            }
    
            Func<IDataReader, object, TRet> CreateMultiPocoFactory<TRet>(Type[] types, string sql, IDataReader r)
            {
                var m = new DynamicMethod("petapoco_multipoco_factory", typeof(TRet), new Type[] { typeof(MultiPocoFactory), typeof(IDataReader), typeof(object) }, typeof(MultiPocoFactory));
                var il = m.GetILGenerator();
    
    
                il.Emit(OpCodes.Ldarg_2);
    
                var dels = new List<Delegate>();
                int pos = 0;
                for (int i = 0; i < types.Length; i++)
                {
    
                    var del = FindSplitPoint(types[i], i + 1 < types.Length ? types[i + 1] : null, sql, r, ref pos);
                    dels.Add(del);
    
    
                    il.Emit(OpCodes.Ldarg_0);                                                    // callback,this
                    il.Emit(OpCodes.Ldc_I4, i);                                                    // callback,this,Index
                    il.Emit(OpCodes.Callvirt, typeof(MultiPocoFactory).GetMethod("GetItem"));    // callback,Delegate
                    il.Emit(OpCodes.Ldarg_1);                                                    // callback,delegate, datareader
    
    
                    var tDelInvoke = del.GetType().GetMethod("Invoke");
                    il.Emit(OpCodes.Callvirt, tDelInvoke);                                        // Poco left on stack
                }
    
    
                il.Emit(OpCodes.Callvirt, Expression.GetFuncType(types.Concat(new Type[] { typeof(TRet) }).ToArray()).GetMethod("Invoke"));
                il.Emit(OpCodes.Ret);
    
    
                return (Func<IDataReader, object, TRet>)m.CreateDelegate(typeof(Func<IDataReader, object, TRet>), new MultiPocoFactory() { m_Delegates = dels });
            }
            static Dictionary<string, object> MultiPocoFactories = new Dictionary<string, object>();
            static Dictionary<string, object> AutoMappers = new Dictionary<string, object>();
            static System.Threading.ReaderWriterLockSlim RWLock = new System.Threading.ReaderWriterLockSlim();
    
            Func<IDataReader, object, TRet> GetMultiPocoFactory<TRet>(Type[] types, string sql, IDataReader r)
            {
                //不建议使用,特别是在频繁使用时
                var kb = new StringBuilder();
                kb.Append(typeof(TRet).ToString());
                kb.Append(":");
                foreach (var t in types)
                {
                    kb.Append(":");
                    kb.Append(t.ToString());
                }
                kb.Append(":"); kb.Append(_sharedConnection.ConnectionString);
                kb.Append(":"); kb.Append(ForceDateTimesToUtc);
    
                string conname = string.Empty;
                for (var i = 0; i < r.FieldCount; i++)
                {
                    conname = conname + r.GetName(i) + r.GetFieldType(i).ToString().Replace("System.", string.Empty);
                }
                kb.Append(":"); kb.Append(conname);
                string key = kb.ToString();
    
                // Check cache
                RWLock.EnterReadLock();
                try
                {
                    object oFactory;
                    if (MultiPocoFactories.TryGetValue(key, out oFactory))
                        return (Func<IDataReader, object, TRet>)oFactory;
                }
                finally
                {
                    RWLock.ExitReadLock();
                }
    
                // Cache it
                RWLock.EnterWriteLock();
                try
                {
                    // Check again
                    object oFactory;
                    if (MultiPocoFactories.TryGetValue(key, out oFactory))
                        return (Func<IDataReader, object, TRet>)oFactory;
    
                    // Create the factory
                    var Factory = CreateMultiPocoFactory<TRet>(types, sql, r);
    
                    MultiPocoFactories.Add(key, Factory);
                    return Factory;
                }
                finally
                {
                    RWLock.ExitWriteLock();
                }
    
            }
    
            public IEnumerable<TRet> Query<TRet>(Type[] types, object cb, string sql, params object[] args)
            {
                OpenSharedConnection();
                try
                {
                    using (var cmd = CreateCommand(_sharedConnection, sql, args))
                    {
                        using (IDataReader r = cmd.ExecuteReader())
                        {
                            try
                            {
                                OnExecutedCommand(cmd);
                            }
                            catch (Exception x)
                            {
                                OnException(x);
                                throw;
                            }
                            var factory = GetMultiPocoFactory<TRet>(types, sql, r);
                            if (cb == null)
                                cb = GetAutoMapper(types.ToArray());
                            bool bNeedTerminator = false;
                            while (true)
                            {
                                TRet poco;
                                try
                                {
                                    if (!r.Read())
                                        break;
                                    poco = factory(r, cb);
                                }
                                catch (Exception x)
                                {
                                    OnException(x);
                                    throw;
                                }
    
                                if (poco != null)
                                    yield return poco;
                                else
                                    bNeedTerminator = true;
                            }
                            if (bNeedTerminator)
                            {
                                var poco = (TRet)(cb as Delegate).DynamicInvoke(new object[types.Length]);
                                if (poco != null)
                                    yield return poco;
                                else
                                    yield break;
                            }
                        }
                    }
                }
                finally
                {
                    CloseSharedConnection();
                }
            }
    
            public IEnumerable<T> Query<T>(Sql sql)
            {
                return Query<T>(sql.SQL, sql.Arguments);
            }
    
            /// <summary>
            /// 查询优化
            /// </summary>
            /// <typeparam name="T">实体类型</typeparam>
            /// <param name="sql">Where条件</param>
            /// <param name="expressions">Lamda表达式</param>
            /// <returns></returns>
            public IEnumerable<T> QueryExtend<T>(string sql, params Expression<Func<T, object>>[] expressions)
            {
                string cols = "*";
                if (expressions != null && expressions.Length > 0)
                {
                    cols = string.Join(", ", (from c in expressions select GetTableColumn(c)).ToArray());
                }
                sql = !rxFrom.IsMatch(sql) ? string.Format("SELECT {0} FROM {1} with(nolock) {2}", cols, GetTableName<T>(), sql) : string.Format("SELECT {0} with(nolock) {1}", cols, sql);
                OpenSharedConnection();
                try
                {
                    using (var cmd = CreateCommand(_sharedConnection, sql, null))
                    {
                        var pd = PocoData.ForType(typeof(T), TablePerfixFunc, TablePerfix);
                        using (IDataReader r = cmd.ExecuteReader())
                        {
                            try
                            {
                                OnExecutedCommand(cmd);
                            }
                            catch (Exception x)
                            {
                                OnException(x);
                                throw;
                            }
                            var factory =
                                pd.GetFactory(cmd.CommandText, _sharedConnection.ConnectionString, ForceDateTimesToUtc, 0,
                                              r.FieldCount, r) as Func<IDataReader, T>;
                            while (true)
                            {
                                T poco;
                                try
                                {
                                    if (!r.Read())
                                    {
                                        yield break;
                                    }
                                    poco = factory(r);
                                }
                                catch (Exception x)
                                {
                                    OnException(x);
                                    throw;
                                }
                                yield return poco;
                            }
                        }
                    }
                }
                finally
                {
                    CloseSharedConnection();
                }
            }
    
            /// <summary>
            /// 查询优化
            /// </summary>
            /// <typeparam name="T">实体类型</typeparam>
            /// <param name="sql">Where条件</param>
            /// <param name="args">参数</param>
            /// <param name="expressions">Lamda表达式</param>
            /// <returns></returns>
            public IEnumerable<T> QueryExtend<T>(string sql, object[] args, params Expression<Func<T, object>>[] expressions)
            {
                string cols = "*";
                if (expressions != null && expressions.Length > 0)
                {
                    cols = string.Join(", ", (from c in expressions select GetTableColumn(c)).ToArray());
                }
                sql = !rxFrom.IsMatch(sql) ? string.Format("SELECT {0} FROM {1} with(nolock) {2}", cols, GetTableName<T>(), sql) : string.Format("SELECT {0} with(nolock) {1}", cols, sql);
                OpenSharedConnection();
                try
                {
                    using (var cmd = CreateCommand(_sharedConnection, sql, args))
                    {
                        var pd = PocoData.ForType(typeof(T), TablePerfixFunc, TablePerfix);
                        using (IDataReader r = cmd.ExecuteReader())
                        {
                            try
                            {
                                OnExecutedCommand(cmd);
                            }
                            catch (Exception x)
                            {
                                OnException(x);
                                throw;
                            }
                            var factory =
                                pd.GetFactory(cmd.CommandText, _sharedConnection.ConnectionString, ForceDateTimesToUtc, 0,
                                              r.FieldCount, r) as Func<IDataReader, T>;
                            while (true)
                            {
                                T poco;
                                try
                                {
                                    if (!r.Read())
                                    {
                                        yield break;
                                    }
                                    poco = factory(r);
                                }
                                catch (Exception x)
                                {
                                    OnException(x);
                                    throw;
                                }
                                yield return poco;
                            }
                        }
                    }
                }
                finally
                {
                    CloseSharedConnection();
                }
            }
    
            public bool Exists<T>(object primaryKey)
            {
                return FirstOrDefault<T>(string.Format("WHERE {0}=@0", EscapeSqlIdentifier(PocoData.ForType(typeof(T), TablePerfixFunc, TablePerfix).TableInfo.PrimaryKey)), primaryKey) != null;
            }
            public T SingleWithKey<T>(object primaryKey)
            {
                return Single<T>(string.Format("WHERE {0}=@0", EscapeSqlIdentifier(PocoData.ForType(typeof(T), TablePerfixFunc, TablePerfix, true).TableInfo.PrimaryKey)), primaryKey);
            }
            public T SingleOrDefaultWithKey<T>(object primaryKey)
            {
                return SingleOrDefault<T>(string.Format("WHERE {0}=@0", EscapeSqlIdentifier(PocoData.ForType(typeof(T), TablePerfixFunc, TablePerfix, true).TableInfo.PrimaryKey)), primaryKey);
            }
            public T Single<T>(string sql, params object[] args)
            {
                return Query<T>(true, sql, args).Single();
            }
            public T SingleOrDefault<T>(string sql, params object[] args)
            {
                return Query<T>(true, sql, args).FirstOrDefault();
            }
            public T SingleOrDefault<T>(bool isIgnoreResultColumn, string sql, params object[] args)
            {
                return Query<T>(isIgnoreResultColumn, sql, args).FirstOrDefault();
            }
            public T First<T>(string sql, params object[] args)
            {
                return Query<T>(sql, args).First();
            }
            public T FirstOrDefault<T>(string sql, params object[] args)
            {
                return Query<T>(true, sql, args).FirstOrDefault();
            }
            public T Single<T>(Sql sql)
            {
                return Query<T>(sql).Single();
            }
            public T SingleOrDefault<T>(Sql sql)
            {
                return Query<T>(sql).SingleOrDefault();
            }
            public T First<T>(Sql sql)
            {
                return Query<T>(sql).First();
            }
            public T FirstOrDefault<T>(Sql sql)
            {
                return Query<T>(sql).FirstOrDefault();
            }
            public string EscapeTableName(string str)
            {
                return str.IndexOf('.') >= 0 ? str : EscapeSqlIdentifier(str);
            }
            public string EscapeSqlIdentifier(string str)
            {
                switch (_dbType)
                {
                    default:
                        return string.Format("[{0}]", str);
                }
            }
    
            public object Insert(string tableName, string primaryKeyName, object poco)
            {
                return Insert(tableName, primaryKeyName, true, poco);
            }
    
            public object Insert(string tableName, string primaryKeyName, bool autoIncrement, object poco)
            {
                try
                {
                    OpenSharedConnection();
                    try
                    {
                        using (var cmd = CreateCommand(_sharedConnection, ""))
                        {
                            var pd = PocoData.ForObject(poco, primaryKeyName, TablePerfixFunc, TablePerfix);
                            var names = new List<string>();
                            var values = new List<string>();
                            var index = 0;
                            foreach (var i in pd.Columns)
                            {
                                // Don't insert result columns
                                if (i.Value.ResultColumn)
                                    continue;
    
                                //add by jqw 20160811新增过滤null值的字段
                                var item = i.Value.GetValue(poco);
                                if (item == null)
                                {
                                    continue;
                                }
                                // Don't insert the primary key (except under oracle where we need bring in the next sequence value)
                                var primaryKeyType = i.Value.GetValue(poco).GetType();
                                if (autoIncrement && primaryKeyName != null && string.Compare(i.Key, primaryKeyName, true) == 0 && (primaryKeyType == typeof(Int32) || primaryKeyType == typeof(int) || primaryKeyType == typeof(Int64)))
                                {
                                    continue;
                                }
                                //add by jqw 20160811新增过滤时间为0001-01-01值的字段
                                var t = item.GetType();
                                if (t == typeof(DateTime))
                                {
                                    if (((DateTime)item).Year == 1)
                                    {
                                        continue;
                                    }
                                }
                                names.Add(EscapeSqlIdentifier(i.Key));
                                values.Add(string.Format("{0}{1}", _paramPrefix, index++));
                                AddParam(cmd, i.Value.GetValue(poco), _paramPrefix);
                            }
    
                            cmd.CommandText = string.Format("INSERT INTO {0} ({1}) VALUES ({2})",
                                    EscapeTableName(tableName),
                                    string.Join(",", names.ToArray()),
                                    string.Join(",", values.ToArray())
                                    );
    
                            if (!autoIncrement)
                            {
                                DoPreExecute(cmd);
                                cmd.ExecuteNonQuery();
                                OnExecutedCommand(cmd);
                                return true;
                            }
                            object id;
                            switch (_dbType)
                            {
                                case DBType.SqlServer:
                                    //cmd.CommandText += ";
    SELECT SCOPE_IDENTITY() AS NewID;";
                                    cmd.CommandText += ";";
                                    DoPreExecute(cmd);
                                    //id = cmd.ExecuteScalar();
                                    id = cmd.ExecuteNonQuery();
                                    OnExecutedCommand(cmd);
                                    break;
                                default:
                                    //cmd.CommandText += ";
    SELECT @@IDENTITY AS NewID;";
                                    cmd.CommandText += ";";
                                    DoPreExecute(cmd);
                                    id = cmd.ExecuteScalar();
                                    OnExecutedCommand(cmd);
                                    break;
                            }
                            // Assign the ID back to the primary key property
                            if (primaryKeyName != null)
                            {
                                PocoColumn pc;
                                if (pd.Columns.TryGetValue(primaryKeyName, out pc))
                                {
                                    pc.SetValue(poco, pc.ChangeType(id));
                                }
                            }
                            return id;
                        }
                    }
                    finally
                    {
                        CloseSharedConnection();
                    }
                }
                catch (Exception x)
                {
                    OnException(x);
                    throw;
                }
            }
    
            public object InsertGetId(string tableName, string primaryKeyName, bool autoIncrement, object poco)
            {
                try
                {
                    OpenSharedConnection();
                    try
                    {
                        using (var cmd = CreateCommand(_sharedConnection, ""))
                        {
                            var pd = PocoData.ForObject(poco, primaryKeyName, TablePerfixFunc, TablePerfix);
                            var names = new List<string>();
                            var values = new List<string>();
                            var index = 0;
                            foreach (var i in pd.Columns)
                            {
                                // Don't insert result columns
                                if (i.Value.ResultColumn)
                                    continue;
    
                                //add by jqw 20160811新增过滤null值的字段
                                var item = i.Value.GetValue(poco);
                                if (item == null)
                                {
                                    continue;
                                }
                                // Don't insert the primary key (except under oracle where we need bring in the next sequence value)
                                var primaryKeyType = i.Value.GetValue(poco).GetType();
                                if (autoIncrement && primaryKeyName != null && string.Compare(i.Key, primaryKeyName, true) == 0 && (primaryKeyType == typeof(Int32) || primaryKeyType == typeof(int) || primaryKeyType == typeof(Int64)))
                                {
                                    continue;
                                }
                                //add by jqw 20160811新增过滤时间为0001-01-01值的字段
                                var t = item.GetType();
                                if (t == typeof(DateTime))
                                {
                                    if (((DateTime)item).Year == 1)
                                    {
                                        continue;
                                    }
                                }
                                names.Add(EscapeSqlIdentifier(i.Key));
                                values.Add(string.Format("{0}{1}", _paramPrefix, index++));
                                AddParam(cmd, i.Value.GetValue(poco), _paramPrefix);
                            }
    
                            cmd.CommandText = string.Format("INSERT INTO {0} ({1}) VALUES ({2})",
                                    EscapeTableName(tableName),
                                    string.Join(",", names.ToArray()),
                                    string.Join(",", values.ToArray())
                                    );
    
                            if (!autoIncrement)
                            {
                                DoPreExecute(cmd);
                                cmd.ExecuteNonQuery();
                                OnExecutedCommand(cmd);
                                return true;
                            }
                            object id;
                            switch (_dbType)
                            {
                                case DBType.SqlServer:
                                    cmd.CommandText += ";
    SELECT SCOPE_IDENTITY() AS NewID";
                                    cmd.CommandText += ";";
                                    DoPreExecute(cmd);
                                    id = cmd.ExecuteScalar();
                                    //id = cmd.ExecuteNonQuery();
                                    OnExecutedCommand(cmd);
                                    break;
                                default:
                                    //cmd.CommandText += ";
    SELECT @@IDENTITY AS NewID;";
                                    cmd.CommandText += ";";
                                    DoPreExecute(cmd);
                                    id = cmd.ExecuteScalar();
                                    OnExecutedCommand(cmd);
                                    break;
                            }
                            // Assign the ID back to the primary key property
                            if (primaryKeyName != null)
                            {
                                PocoColumn pc;
                                if (pd.Columns.TryGetValue(primaryKeyName, out pc))
                                {
                                    pc.SetValue(poco, pc.ChangeType(id));
                                }
                            }
                            return id;
                        }
                    }
                    finally
                    {
                        CloseSharedConnection();
                    }
                }
                catch (Exception x)
                {
                    OnException(x);
                    throw;
                }
            }
    
            public object Insert(object poco)
            {
                var pd = PocoData.ForType(poco.GetType(), TablePerfixFunc, TablePerfix);
                return Insert(pd.TableInfo.TableName, pd.TableInfo.PrimaryKey, pd.TableInfo.AutoIncrement, poco);
            }
    
            public object InsertGetId(object poco)
            {
                var pd = PocoData.ForType(poco.GetType(), TablePerfixFunc, TablePerfix);
                return InsertGetId(pd.TableInfo.TableName, pd.TableInfo.PrimaryKey, pd.TableInfo.AutoIncrement, poco);
            }
    
            public int Update(string tableName, string primaryKeyName, object poco, object primaryKeyValue)
            {
                return Update(tableName, primaryKeyName, poco, primaryKeyValue, null);
            }
    
            public int Update(string tableName, string primaryKeyName, object poco, object primaryKeyValue, IEnumerable<string> columns)
            {
                try
                {
                    OpenSharedConnection();
                    try
                    {
                        using (var cmd = CreateCommand(_sharedConnection, ""))
                        {
                            var sb = new StringBuilder();
                            var index = 0;
                            var pd = PocoData.ForObject(poco, primaryKeyName, TablePerfixFunc, TablePerfix);
                            if (columns == null || columns.Count().Equals(0))
                            {
                                foreach (var i in pd.Columns)
                                {
                                    // Don't update the primary key, but grab the value if we don't have it
                                    if (string.Compare(i.Key, primaryKeyName, true) == 0)
                                    {
                                        if (primaryKeyValue == null)
                                            primaryKeyValue = i.Value.GetValue(poco);
                                        continue;
                                    }
                                    // Dont update result only columns
                                    if (i.Value.ResultColumn)
                                        continue;
                                    // Build the sql
                                    if (index > 0)
                                        sb.Append(", ");
                                    sb.AppendFormat("{0} = {1}{2}", EscapeSqlIdentifier(i.Key), _paramPrefix, index++);
                                    // Store the parameter in the command
                                    AddParam(cmd, i.Value.GetValue(poco), _paramPrefix);
                                }
                            }
                            else
                            {
                                foreach (var colname in columns)
                                {
                                    var pc = pd.Columns[colname];
                                    // Build the sql
                                    if (index > 0)
                                        sb.Append(", ");
                                    sb.AppendFormat("{0} = {1}{2}", EscapeSqlIdentifier(colname), _paramPrefix, index++);
                                    // Store the parameter in the command
                                    AddParam(cmd, pc.GetValue(poco), _paramPrefix);
                                }
                                // Grab primary key value
                                if (primaryKeyValue == null)
                                {
                                    var pc = pd.Columns[primaryKeyName];
                                    primaryKeyValue = pc.GetValue(poco);
                                }
                            }
                            cmd.CommandText = string.Format("UPDATE {0} SET {1} WHERE {2} = {3}{4}",
                                                EscapeTableName(tableName), sb.ToString(), EscapeSqlIdentifier(primaryKeyName), _paramPrefix, index++);
                            AddParam(cmd, primaryKeyValue, _paramPrefix);
                            DoPreExecute(cmd);
                            // Do it
                            var retv = cmd.ExecuteNonQuery();
                            OnExecutedCommand(cmd);
                            return retv;
                        }
                    }
                    finally
                    {
                        CloseSharedConnection();
                    }
                }
                catch (Exception x)
                {
                    OnException(x);
                    throw;
                }
            }
    
            public int Update(string tableName, string primaryKeyName, object poco)
            {
                return Update(tableName, primaryKeyName, poco, null);
            }
    
            public int Update(string tableName, string primaryKeyName, object poco, IEnumerable<string> columns)
            {
                return Update(tableName, primaryKeyName, poco, null, columns);
            }
    
            public int Update<TModel>(object poco, params Expression<Func<TModel, object>>[] expressions)
            {
                return Update(poco, null, (from c in expressions select GetTableColumn(c)).ToArray());
            }
    
            public int Update(object poco, IEnumerable<string> columns)
            {
                return Update(poco, null, columns);
            }
    
            public int Update(object poco)
            {
                return Update(poco, null, null);
            }
    
            public int Update(object poco, object primaryKeyValue)
            {
                return Update(poco, primaryKeyValue, null);
            }
            public int Update(object poco, object primaryKeyValue, IEnumerable<string> columns)
            {
                var pd = PocoData.ForType(poco.GetType(), TablePerfixFunc, TablePerfix);
                return Update(pd.TableInfo.TableName, pd.TableInfo.PrimaryKey, poco, primaryKeyValue, columns);
            }
    
            public int Update<T>(string sql, params object[] args)
            {
                var pd = PocoData.ForType(typeof(T), TablePerfixFunc, TablePerfix);
                return Execute(string.Format("UPDATE {0} {1}", EscapeTableName(pd.TableInfo.TableName), sql), args);
            }
    
            public int Update<T>(Sql sql)
            {
                var pd = PocoData.ForType(typeof(T), TablePerfixFunc, TablePerfix);
                return Execute(new Sql(string.Format("UPDATE {0}", EscapeTableName(pd.TableInfo.TableName))).Append(sql));
            }
    
            /// <summary>
            /// 实体更新,具有版本控制功能
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="item"></param>
            /// <param name="needVersionControl"></param>
            /// <returns></returns>
            public int Update<T>(T item, bool needVersionControl)
            {
                try
                {
                    OpenSharedConnection();
                    try
                    {
                        using (var cmd = CreateCommand(_sharedConnection, ""))
                        {
                            var sb = new StringBuilder();
                            var index = 0;
                            var pd = PocoData.ForType(typeof(T), TablePerfixFunc, TablePerfix);
                            var primaryKeyName = pd.TableInfo.PrimaryKey;
                            object primaryKeyValue = null;
                            var tableName = pd.TableInfo.TableName;
                            foreach (var i in pd.Columns)
                            {
                                // Don't update the primary key, but grab the value if we don't have it
                                if (string.Compare(i.Key, primaryKeyName, true) == 0)
                                {
                                    if (primaryKeyValue == null)
                                        primaryKeyValue = i.Value.GetValue(item);
                                    continue;
                                }
                                // Dont update result only columns
                                if (i.Value.ResultColumn)
                                    continue;
                                // Build the sql
                                if (index > 0)
                                    sb.Append(", ");
                                sb.AppendFormat("{0} = {1}{2}", EscapeSqlIdentifier(i.Key), _paramPrefix, index++);
                                // Store the parameter in the command
                                AddParam(cmd, i.Value.GetValue(item), _paramPrefix);
                            }
                            StringBuilder sbVersion = new StringBuilder();
                            if (needVersionControl && pd.Columns.Count(t => t.Key == "Version") > 0)
                            {
                                sbVersion.AppendFormat(" AND Version = {0}{1}", _paramPrefix, index++);
                                AddParam(cmd, pd.Columns["Version"].GetValue(item), _paramPrefix);
                            }
                            cmd.CommandText = string.Format("UPDATE {0} SET {1} WHERE {2} = {3}{4} {5}",
                                                EscapeTableName(tableName), sb.ToString(), EscapeSqlIdentifier(primaryKeyName), _paramPrefix, index++, sbVersion);
                            AddParam(cmd, primaryKeyValue, _paramPrefix);
                            DoPreExecute(cmd);
                            // Do it
                            var retv = cmd.ExecuteNonQuery();
                            OnExecutedCommand(cmd);
                            return retv;
                        }
                    }
                    finally
                    {
                        CloseSharedConnection();
                    }
                }
                catch (Exception x)
                {
                    OnException(x);
                    throw;
                }
            }
    
            public int Delete(string tableName, string primaryKeyName, object poco)
            {
                return Delete(tableName, primaryKeyName, poco, null);
            }
    
            public int Delete(string tableName, string primaryKeyName, object poco, object primaryKeyValue, bool isTureDelete = true)
            {
                // If primary key value not specified, pick it up from the object
                if (primaryKeyValue == null)
                {
                    var pd = PocoData.ForObject(poco, primaryKeyName, TablePerfixFunc, TablePerfix);
                    PocoColumn pc;
                    if (pd.Columns.TryGetValue(primaryKeyName, out pc))
                    {
                        primaryKeyValue = pc.GetValue(poco);
                    }
                }
                // Do it
                var sql = "";
                //是否是真删除
                if (isTureDelete)
                {
                    sql = string.Format("DELETE FROM {0} WHERE {1}=@0", EscapeTableName(tableName), EscapeSqlIdentifier(primaryKeyName));
                }
                else
                {
                    sql = string.Format("UPDATE {0} SET ROWSTATUS=-1 WHERE {1}=@0", EscapeTableName(tableName), EscapeSqlIdentifier(primaryKeyName));
                }
                return Execute(sql, primaryKeyValue);
            }
    
            //jqw 20160728
            public int DeleteBatch(string tableName, string primaryKeyName, object poco, object primaryKeyValue, bool isTureDelete = true)
            {
                // If primary key value not specified, pick it up from the object
                if (primaryKeyValue == null)
                {
                    var pd = PocoData.ForObject(poco, primaryKeyName, TablePerfixFunc, TablePerfix);
                    PocoColumn pc;
                    if (pd.Columns.TryGetValue(primaryKeyName, out pc))
                    {
                        primaryKeyValue = pc.GetValue(poco);
                    }
                }
                // Do it
                var sql = "";
                //是否是真删除
                if (isTureDelete)
                {
                    sql = string.Format("DELETE FROM {0} WHERE {1} in({2}) ", EscapeTableName(tableName), EscapeSqlIdentifier(primaryKeyName), primaryKeyValue);
                }
                else
                {
                    sql = string.Format("UPDATE {0} SET ROWSTATUS=-1 WHERE {1} in({2}) ", EscapeTableName(tableName), EscapeSqlIdentifier(primaryKeyName), primaryKeyValue);
                }
                return Execute(sql, primaryKeyValue);
            }
    
            public int Delete(object poco)
            {
                var pd = PocoData.ForType(poco.GetType(), TablePerfixFunc, TablePerfix);
                return Delete(pd.TableInfo.TableName, pd.TableInfo.PrimaryKey, poco);
            }
    
            /// <summary>
            ///  假删除,修改RowStatus
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="pocoOrPrimaryKey"></param>
            /// <returns></returns>
            public int DeleteFalse<T>(object pocoOrPrimaryKey)
            {
                if (pocoOrPrimaryKey.GetType() == typeof(T))
                    return Delete(pocoOrPrimaryKey);
                var pd = PocoData.ForType(typeof(T), TablePerfixFunc, TablePerfix);
                return Delete(pd.TableInfo.TableName, pd.TableInfo.PrimaryKey, null, pocoOrPrimaryKey, false);
            }
    
            //jqw 20160728
            public int DeleteFalseBatch<T>(object pocoOrPrimaryKeys)
            {
                if (pocoOrPrimaryKeys.GetType() == typeof(T))
                    return Delete(pocoOrPrimaryKeys);
                var pd = PocoData.ForType(typeof(T), TablePerfixFunc, TablePerfix);
                return DeleteBatch(pd.TableInfo.TableName, pd.TableInfo.PrimaryKey, null, pocoOrPrimaryKeys, false);
            }
    
            public int Delete<T>(object pocoOrPrimaryKey)
            {
                if (pocoOrPrimaryKey.GetType() == typeof(T))
                    return Delete(pocoOrPrimaryKey);
                var pd = PocoData.ForType(typeof(T), TablePerfixFunc, TablePerfix);
                return Delete(pd.TableInfo.TableName, pd.TableInfo.PrimaryKey, null, pocoOrPrimaryKey);
            }
    
            //jqw 20160728
            public int DeleteBatch<T>(object pocoOrPrimaryKeys)
            {
                if (pocoOrPrimaryKeys.GetType() == typeof(T))
                    return Delete(pocoOrPrimaryKeys);
                var pd = PocoData.ForType(typeof(T), TablePerfixFunc, TablePerfix);
                return DeleteBatch(pd.TableInfo.TableName, pd.TableInfo.PrimaryKey, null, pocoOrPrimaryKeys);
            }
    
            public int Delete<T>(string sql, params object[] args)
            {
                var pd = PocoData.ForType(typeof(T), TablePerfixFunc, TablePerfix);
                return Execute(string.Format("DELETE FROM {0} {1}", EscapeTableName(pd.TableInfo.TableName), sql), args);
            }
    
            public int Delete<T>(Sql sql)
            {
                var pd = PocoData.ForType(typeof(T), TablePerfixFunc, TablePerfix);
                return Execute(new Sql(string.Format("DELETE FROM {0}", EscapeTableName(pd.TableInfo.TableName))).Append(sql));
            }
    
            public bool IsNew(string primaryKeyName, object poco)
            {
                var pd = PocoData.ForObject(poco, primaryKeyName, TablePerfixFunc, TablePerfix);
                object pk;
                PocoColumn pc;
                if (pd.Columns.TryGetValue(primaryKeyName, out pc))
                {
                    pk = pc.GetValue(poco);
                }
    #if !PETAPOCO_NO_DYNAMIC
                else if (poco.GetType() == typeof(System.Dynamic.ExpandoObject))
                {
                    return true;
                }
    #endif
                else
                {
                    var pi = poco.GetType().GetProperty(primaryKeyName);
                    if (pi == null)
                        throw new ArgumentException(string.Format("The object doesn't have a property matching the primary key column name '{0}'", primaryKeyName));
                    pk = pi.GetValue(poco, null);
                }
                if (pk == null)
                    return true;
                var type = pk.GetType();
                if (type.IsValueType)
                {
                    // Common primary key types
                    if (type == typeof(long))
                        return (long)pk == 0;
                    else if (type == typeof(ulong))
                        return (ulong)pk == 0;
                    else if (type == typeof(int))
                        return (int)pk == 0;
                    else if (type == typeof(uint))
                        return (uint)pk == 0;
    
                    // Create a default instance and compare
                    return pk == Activator.CreateInstance(pk.GetType());
                }
                else
                {
                    return pk == null;
                }
            }
    
            public bool IsNew(object poco)
            {
                var pd = PocoData.ForType(poco.GetType(), TablePerfixFunc, TablePerfix);
                if (!pd.TableInfo.AutoIncrement)
                    throw new InvalidOperationException("IsNew() and Save() are only supported on tables with auto-increment/identity primary key columns");
                return IsNew(pd.TableInfo.PrimaryKey, poco);
            }
    
            public void Save(string tableName, string primaryKeyName, object poco)
            {
                if (IsNew(primaryKeyName, poco))
                {
                    Insert(tableName, primaryKeyName, true, poco);
                }
                else
                {
                    Update(tableName, primaryKeyName, poco);
                }
            }
    
            public void Save(object poco)
            {
                var pd = PocoData.ForType(poco.GetType(), TablePerfixFunc, TablePerfix);
                Save(pd.TableInfo.TableName, pd.TableInfo.PrimaryKey, poco);
            }
    
            #region 批量更新数据
    
            private StringBuilder batchSql = new StringBuilder();
            private int batIndex = 0;
            private IDbCommand batchCommand;
            private IDbCommand GetBatchCommand()
            {
                if (batchCommand == null)
                    batchCommand = new SqlCommand();
                return batchCommand;
            }
    
            /// <summary>
            /// 批量插入实体
            /// </summary>
            /// <param name="poco"></param>
            public void BatchInsert(object poco)
            {
                var pd = PocoData.ForType(poco.GetType(), TablePerfixFunc, TablePerfix);
                var names = new List<string>();
                var values = new List<string>();
                foreach (var i in pd.Columns)
                {
                    if (i.Value.ResultColumn)
                        continue;
                    if (pd.TableInfo.AutoIncrement && pd.TableInfo.PrimaryKey != null && string.Compare(i.Key, pd.TableInfo.PrimaryKey, true) == 0)
                    {
                        continue;
                    }
                    names.Add(EscapeSqlIdentifier(i.Key));
                    values.Add(string.Format("{0}{1}", _paramPrefix, batIndex++));
                    AddParam(GetBatchCommand(), i.Value.GetValue(poco), _paramPrefix);
                }
                batchSql.AppendLine(string.Format("INSERT INTO {0} ({1}) VALUES ({2})",
                    EscapeTableName(pd.TableInfo.TableName),
                    string.Join(",", names.ToArray()),
                    string.Join(",", values.ToArray())
                    ));
            }
    
            /// <summary>
            /// 批量插入实体
            /// </summary>
            /// <param name="poco"></param>
            /// <param name="withNoParameters">生成的SQL Script是否要参数化(false:需要,true:不需要)</param>
            public void BatchInsert(object poco, bool withNoParameters)
            {
                if (!withNoParameters)
                {
                    BatchInsert(poco);
                }
                else
                {
                    var pd = PocoData.ForType(poco.GetType(), TablePerfixFunc, TablePerfix);
                    var names = new List<string>();
                    var values = new List<string>();
                    foreach (var i in pd.Columns)
                    {
                        if (i.Value.ResultColumn)
                            continue;
                        if (pd.TableInfo.AutoIncrement && pd.TableInfo.PrimaryKey != null && string.Compare(i.Key, pd.TableInfo.PrimaryKey, true) == 0)
                        {
                            continue;
                        }
                        names.Add(EscapeSqlIdentifier(i.Key));
                        values.Add(GetSqlScriptValue(i.Value, poco));
                    }
                    batchSql.AppendLine(string.Format("INSERT INTO {0} ({1}) VALUES ({2})",
                        EscapeTableName(pd.TableInfo.TableName),
                        string.Join(",", names.ToArray()),
                        string.Join(",", values.ToArray())
                        ));
                }
            }
    
            /// <summary>
            /// 生成SQL SCRIPT 对应的字段值
            /// </summary>
            /// <param name="column"></param>
            /// <param name="poco"></param>
            /// <returns></returns>
            private string GetSqlScriptValue(PocoColumn column, object poco)
            {
                switch (column.PropertyInfo.PropertyType.Name.ToLower())
                {
                    case "string": return string.Format("'{0}'", column.GetValue(poco));
                    case "datetime":
                        var dt = (DateTime)column.GetValue(poco);
                        return string.Format("'{0}'", (dt.Year.Equals(1)) ? "1900-01-01" : dt.ToString());
                    case "date": return string.Format("'{0}'", column.GetValue(poco));
                    case "time": return string.Format("'{0}'", column.GetValue(poco));
                    default: return string.Format("{0}", column.GetValue(poco));
                }
            }
    
            /// <summary>
            /// 批量更新实体
            /// </summary>
            /// <param name="poco"></param>
            /// <param name="expressions">LAMDA</param>
            public void BatchUpdate<T>(T poco, params Expression<Func<T, object>>[] expressions)
            {
                var pd = PocoData.ForType(poco.GetType(), TablePerfixFunc, TablePerfix);
                var sb = new StringBuilder();
                var index = 0;
                if (expressions != null && expressions.Length > 0)
                {
                    var columns = (from c in expressions select GetTableColumn(c)).ToArray();
                    foreach (var colname in columns)
                    {
                        var pc = pd.Columns[colname];
                        if (index > 0)
                            sb.Append(", ");
                        sb.AppendFormat("{0} = {1}{2}", EscapeSqlIdentifier(colname), _paramPrefix, batIndex++);
                        index += 1;
                        AddParam(GetBatchCommand(), pc.GetValue(poco), _paramPrefix);
                    }
                }
                else
                {
                    foreach (var i in pd.Columns)
                    {
                        // Don't update the primary key, but grab the value if we don't have it
                        if (string.Compare(i.Key, pd.TableInfo.PrimaryKey, true) == 0)
                        {
                            continue;
                        }
                        // Dont update result only columns
                        if (i.Value.ResultColumn)
                            continue;
                        // Build the sql
                        if (index > 0)
                            sb.Append(", ");
                        sb.AppendFormat("{0} = {1}{2}", EscapeSqlIdentifier(i.Key), _paramPrefix, batIndex++);
                        // Store the parameter in the command
                        AddParam(GetBatchCommand(), i.Value.GetValue(poco), _paramPrefix);
                        index++;
                    }
                }
                batchSql.AppendLine(string.Format("UPDATE {0} SET {1} WHERE {2} = {3}{4}",
                                    EscapeTableName(pd.TableInfo.TableName), sb, EscapeSqlIdentifier(pd.TableInfo.PrimaryKey), _paramPrefix, batIndex++));
                AddParam(GetBatchCommand(), pd.Columns[pd.TableInfo.PrimaryKey].GetValue(poco), _paramPrefix);
            }
    
            /// <summary>
            /// 批量更新实体
            /// </summary>
            /// <param name="poco">实体</param>
            /// <param name="withNoParameters">生成的SQL Script是否要参数化(false:需要,true:不需要)</param>
            /// <param name="expressions">LAMDA</param>
            public void BatchUpdate<T>(T poco, bool withNoParameters, params Expression<Func<T, object>>[] expressions)
            {
                if (!withNoParameters)
                {
                    BatchUpdate(poco, expressions);
                }
                else
                {
                    var pd = PocoData.ForType(poco.GetType(), TablePerfixFunc, TablePerfix);
                    var sb = new StringBuilder();
                    var index = 0;
                    if (expressions != null && expressions.Length > 0)
                    {
                        var columns = (from c in expressions select GetTableColumn(c)).ToArray();
                        foreach (var colname in columns)
                        {
                            var pc = pd.Columns[colname];
                            if (index > 0)
                                sb.Append(", ");
                            sb.AppendFormat("{0} = {1}", EscapeSqlIdentifier(colname), GetSqlScriptValue(pc, poco));
                            index += 1;
                        }
                    }
                    else
                    {
                        foreach (var i in pd.Columns)
                        {
                            // Don't update the primary key, but grab the value if we don't have it
                            if (string.Compare(i.Key, pd.TableInfo.PrimaryKey, true) == 0)
                            {
                                continue;
                            }
                            // Dont update result only columns
                            if (i.Value.ResultColumn)
                                continue;
                            // Build the sql
                            if (index > 0)
                                sb.Append(", ");
                            sb.AppendFormat("{0} = {1}", EscapeSqlIdentifier(i.Key), GetSqlScriptValue(i.Value, poco));
                            // Store the parameter in the command
                            index++;
                        }
                    }
                    batchSql.AppendLine(string.Format("UPDATE {0} SET {1} WHERE {2} = {3}",
                                        EscapeTableName(pd.TableInfo.TableName), sb, EscapeSqlIdentifier(pd.TableInfo.PrimaryKey), pd.Columns[pd.TableInfo.PrimaryKey].GetValue(poco)));
                }
            }
    
            /// <summary>
            /// 批量删除实体
            /// </summary>
            /// <param name="poco"></param>
            public void BatchDelete(object poco)
            {
                var pd = PocoData.ForType(poco.GetType(), TablePerfixFunc, TablePerfix);
                batchSql.AppendLine(string.Format("UPDATE {0} SET RowStatus=0 WHERE {1} = {2}{3}",
                                    EscapeTableName(pd.TableInfo.TableName), EscapeSqlIdentifier(pd.TableInfo.PrimaryKey), _paramPrefix, batIndex++));
                AddParam(GetBatchCommand(), pd.Columns[pd.TableInfo.PrimaryKey].GetValue(poco), _paramPrefix);
            }
    
            /// <summary>
            /// SQL批量执行
            /// </summary>
            /// <param name="sql">SQL Script</param>
            public void BatchCommand(string sql)
            {
                batchSql.AppendLine(sql);
            }
    
            /// <summary>
            /// 批量执行
            /// </summary>
            /// <returns></returns>
            public int BatchExcute()
            {
                try
                {
                    OpenSharedConnection();
                    try
                    {
                        using (var cmd = GetBatchCommand())
                        {
                            cmd.CommandText = batchSql.ToString();
                            cmd.CommandType = CommandType.Text;
                            cmd.Connection = _sharedConnection;
                            cmd.Transaction = _transaction;
                            DoPreExecute(cmd);
                            return cmd.ExecuteNonQuery();
                        }
                    }
                    finally
                    {
                        batchCommand = null;
                        batchSql.Clear();
                        batIndex = 0;
                        CloseSharedConnection();
                    }
                }
                catch (Exception x)
                {
                    OnException(x);
                    throw;
                }
            }
    
    
            #endregion
    
            /// <summary>
            /// 生成SQL Script
            /// </summary>
            /// <typeparam name="TModel"></typeparam>
            /// <param name="fromat"></param>
            /// <param name="expressions"></param>
            /// <returns></returns>
            public static string GetFormatSql<TModel>(string fromat, params Expression<Func<TModel, object>>[] expressions)
            {
                string[] paras = new string[expressions.Length];
                for (var i = 0; i < expressions.Length; i++)
                {
                    paras[i] = GetTableColumn<TModel>(expressions[i]);
                }
                switch (expressions.Length)
                {
                    case 1:
                        return string.Format(fromat, paras[0]);
                    case 2:
                        return string.Format(fromat, paras[0], paras[1]);
                    case 3:
                        return string.Format(fromat, paras[0], paras[1], paras[2]);
                    case 4:
                        return string.Format(fromat, paras[0], paras[1], paras[2], paras[3]);
                    case 5:
                        return string.Format(fromat, paras[0], paras[1], paras[2], paras[3], paras[4]);
                    default:
                        return string.Empty;
                }
            }
    
            public int CommandTimeout { get; set; }
            public int OneTimeCommandTimeout { get; set; }
    
            string AppendComment(string sql)
            {
                //if (Comment == null)
                //{
                //    throw new Exception("注释不能为空!");
                //}
                //else
                //{
                //    sql = sql.AppendComment(Comment);
                //}
                return sql;
            }
    
            void DoPreExecute(IDbCommand cmd)
            {
                if (OneTimeCommandTimeout != 0)
                {
                    cmd.CommandTimeout = OneTimeCommandTimeout;
                    OneTimeCommandTimeout = 0;
                }
                else if (CommandTimeout != 0)
                {
                    cmd.CommandTimeout = CommandTimeout;
                }
                OnExecutingCommand(cmd);
                //2012-2-19 加上的
                //cmd.CommandText = AppendComment(cmd.CommandText);
                _lastSql = AppendComment(cmd.CommandText);
                _lastArgs = (from IDataParameter parameter in cmd.Parameters select parameter.Value).ToArray();
            }
    
            public string LastSQL { get { return _lastSql; } }
            public object[] LastArgs { get { return _lastArgs; } }
            public string LastCommand
            {
                get { return FormatCommand(_lastSql, _lastArgs); }
            }
    
            public string FormatCommand(IDbCommand cmd)
            {
                return FormatCommand(cmd.CommandText, (from IDataParameter parameter in cmd.Parameters select parameter.Value).ToArray());
            }
    
            public string FormatCommand(string sql, object[] args)
            {
                var sb = new StringBuilder();
                if (sql == null)
                    return "";
                sb.Append(sql);
                if (args != null && args.Length > 0)
                {
                    sb.Append("
    ");
                    for (int i = 0; i < args.Length; i++)
                    {
                        sb.AppendFormat("	 -> {0}{1} [{2}] = "{3}"
    ", _paramPrefix, i, args[i].GetType().Name, args[i]);
                    }
                    sb.Remove(sb.Length - 1, 1);
                }
                return sb.ToString();
            }
    
            public static IMapper Mapper
            {
                get;
                set;
            }
    
            public class PocoColumn
            {
                public string ColumnName;
                public PropertyInfo PropertyInfo;
                public bool ResultColumn;
                public virtual void SetValue(object target, object val) { PropertyInfo.SetValue(target, val, null); }
                public virtual object GetValue(object target) { return PropertyInfo.GetValue(target, null); }
                public virtual object ChangeType(object val) { return Convert.ChangeType(val, PropertyInfo.PropertyType); }
            }
            public class ExpandoColumn : PocoColumn
            {
                public override void SetValue(object target, object val) { (target as IDictionary<string, object>)[ColumnName] = val; }
                public override object GetValue(object target)
                {
                    object val = null;
                    (target as IDictionary<string, object>).TryGetValue(ColumnName, out val);
                    return val;
                }
                public override object ChangeType(object val) { return val; }
            }
    
            public Func<string, string, string> TablePerfixFunc { get; set; }
            public string TablePerfix { get; set; }
    
            public class PocoData
            {
                public static PocoData ForObject(object o, string primaryKeyName, Func<string, string, string> TablePerfixFunc, string perfix)
                {
                    var t = o.GetType();
    #if !PETAPOCO_NO_DYNAMIC
                    if (t == typeof(System.Dynamic.ExpandoObject))
                    {
                        var pd = new PocoData();
                        pd.TableInfo = new TableInfo();
                        pd.Columns = new Dictionary<string, PocoColumn>(StringComparer.OrdinalIgnoreCase);
                        pd.Columns.Add(primaryKeyName, new ExpandoColumn() { ColumnName = primaryKeyName });
                        pd.TableInfo.PrimaryKey = primaryKeyName;
                        pd.TableInfo.AutoIncrement = true;
                        foreach (var col in (o as IDictionary<string, object>).Keys)
                        {
                            
                            if (col != primaryKeyName)
                                pd.Columns.Add(col, new ExpandoColumn() { ColumnName = col });
                        }
                        return pd;
                    }
                    else
    #endif
                        return ForType(t, TablePerfixFunc, perfix);
                }
                static System.Threading.ReaderWriterLockSlim RWLock = new System.Threading.ReaderWriterLockSlim();
                public static PocoData ForType(Type t, Func<string, string, string> TablePerfixFunc, string perfix, bool isIgnoreResultColumn = true)
                {
    #if !PETAPOCO_NO_DYNAMIC
                    if (t == typeof(System.Dynamic.ExpandoObject))
                        throw new InvalidOperationException("Can't use dynamic types with this method");
    #endif
                    // Check cache
                    RWLock.EnterReadLock();
                    PocoData pd;
                    try
                    {
                        if (m_PocoDatas.TryGetValue(t, out pd))
                        {
                            if (TablePerfixFunc != null && !String.IsNullOrEmpty(perfix))
                            {
                                pd.TableInfo.TableName = TablePerfixFunc(pd.TableInfo.TableNameBack, perfix);
                            }
                            pd = new PocoData(t, TablePerfixFunc, perfix, isIgnoreResultColumn);
                            return pd;
                        }
                    }
                    finally
                    {
                        RWLock.ExitReadLock();
                    }
                    // Cache it
                    RWLock.EnterWriteLock();
                    try
                    {
                        if (m_PocoDatas.TryGetValue(t, out pd))
                        {
                            if (TablePerfixFunc != null && !String.IsNullOrEmpty(perfix))
                            {
                                pd.TableInfo.TableName = TablePerfixFunc(pd.TableInfo.TableNameBack, perfix);
                            }
                            return pd;
                        }
                        // Create it
                        pd = new PocoData(t, TablePerfixFunc, perfix, isIgnoreResultColumn);
                        m_PocoDatas.Add(t, pd);
                    }
                    finally
                    {
                        RWLock.ExitWriteLock();
                    }
    
                    return pd;
                }
    
                public PocoData()
                {
                }
    
                /// <summary>
                /// 添加参数isIgnoreResultColumn 2015-07-04 by 姜启伟
                /// false 不忽略 true 忽略
                /// </summary>
                /// <param name="t"></param>
                /// <param name="TablePerfixFunc"></param>
                /// <param name="perfix"></param>
                /// <param name="isIgnoreResultColumn"></param>
                public PocoData(Type t, Func<string, string, string> TablePerfixFunc, string perfix, bool isIgnoreResultColumn = true)
                {
                    type = t;
                    TableInfo = new TableInfo();
                    var a = t.GetCustomAttributes(typeof(TableAttribute), true);
                    TableInfo.TableName = a.Length == 0 ? t.Name : (a[0] as TableAttribute).Value;
                    TableInfo.TableNameBack = TableInfo.TableName;
                    if (TablePerfixFunc != null && perfix != null)
                    {
                        TableInfo.TableName = TablePerfixFunc(TableInfo.TableName, perfix);
                    }
                    a = t.GetCustomAttributes(typeof(PrimaryKeyAttribute), true);
                    TableInfo.PrimaryKey = a.Length == 0 ? "ID" : (a[0] as PrimaryKeyAttribute).Value;
                    TableInfo.SequenceName = a.Length == 0 ? null : (a[0] as PrimaryKeyAttribute).SequenceName;
                    TableInfo.AutoIncrement = a.Length == 0 ? false : (a[0] as PrimaryKeyAttribute).AutoIncrement;
                    if (Database.Mapper != null)
                        Database.Mapper.GetTableInfo(t, TableInfo);
                    bool ExplicitColumns = t.GetCustomAttributes(typeof(ExplicitColumnsAttribute), true).Length > 0;
                    Columns = new Dictionary<string, PocoColumn>(StringComparer.OrdinalIgnoreCase);
                    foreach (var pi in t.GetProperties())
                    {
                        var ColAttrs = pi.GetCustomAttributes(typeof(ColumnAttribute), true);
                        if (ExplicitColumns)
                        {
                            if (ColAttrs.Length == 0)
                                continue;
                        }
                        else
                        {
                            if (pi.GetCustomAttributes(typeof(IgnoreAttribute), true).Length != 0)
                                continue;
                        }
                        if (isIgnoreResultColumn)
                        {
                            if (pi.GetCustomAttributes(typeof(ResultColumnAttribute), true).Length != 0)
                                continue;
                        }
                        var pc = new PocoColumn();
                        pc.PropertyInfo = pi;
                        if (ColAttrs.Length > 0)
                        {
                            var colattr = (ColumnAttribute)ColAttrs[0];
                            pc.ColumnName = colattr.Name;
                            if ((colattr as ResultColumnAttribute) != null)
                                pc.ResultColumn = true;
                        }
                        if (pc.ColumnName == null)
                        {
                            pc.ColumnName = pi.Name;
                            if (Database.Mapper != null && !Database.Mapper.MapPropertyToColumn(pi, ref pc.ColumnName, ref pc.ResultColumn))
                                continue;
                        }
                        Columns.Add(pc.ColumnName, pc);
                    }
                    QueryColumns = (from c in Columns select c.Key).ToArray();//where !c.Value.ResultColumn
                }
    
                static bool IsIntegralType(Type t)
                {
                    var tc = Type.GetTypeCode(t);
                    return tc >= TypeCode.SByte && tc <= TypeCode.UInt64;
                }
    
                public Delegate GetFactory(string sql, string connString, bool ForceDateTimesToUtc, int firstColumn, int countColumns, IDataReader r)
                {
                    //修改Key构造方案
                    string conname = string.Empty;
                    for (var i = firstColumn; i < (firstColumn + countColumns); i++)
                    {
                        conname = conname + r.GetName(i) + r.GetFieldType(i).ToString().Replace("System.", string.Empty);
                    }
                    var key = string.Format("{0}:{1}:{2}:{3}:{4}", conname, connString, ForceDateTimesToUtc, firstColumn, countColumns);
                    RWLock.EnterReadLock();
                    try
                    {
                        // Have we already created it?
                        Delegate factory;
                        if (PocoFactories.TryGetValue(key, out factory))
                            return factory;
                    }
                    finally
                    {
                        RWLock.ExitReadLock();
                    }
    
                    // Take the writer lock
                    RWLock.EnterWriteLock();
                    try
                    {
                        Delegate factory;
                        if (PocoFactories.TryGetValue(key, out factory))
                            return factory;
                        var m = new DynamicMethod("petapoco_factory_" + PocoFactories.Count.ToString(), type, new Type[] { typeof(IDataReader) }, true);
                        var il = m.GetILGenerator();
    #if !PETAPOCO_NO_DYNAMIC
                        if (type == typeof(object))
                        {
    
                            il.Emit(OpCodes.Newobj, typeof(System.Dynamic.ExpandoObject).GetConstructor(Type.EmptyTypes));            // obj
    
                            MethodInfo fnAdd = typeof(IDictionary<string, object>).GetMethod("Add");
    
                            for (int i = firstColumn; i < firstColumn + countColumns; i++)
                            {
                                var srcType = r.GetFieldType(i);
                                il.Emit(OpCodes.Dup);                        // obj, obj
                                il.Emit(OpCodes.Ldstr, r.GetName(i));        // obj, obj, fieldname
                                Func<object, object> converter = null;
                                if (Database.Mapper != null)
                                    converter = Database.Mapper.GetFromDbConverter(null, srcType);
                                if (ForceDateTimesToUtc && converter == null && srcType == typeof(DateTime))
                                    converter = delegate (object src) { return new DateTime(((DateTime)src).Ticks, DateTimeKind.Utc); };
                                // Setup stack for call to converter
                                AddConverterToStack(il, converter);
                                // r[i]
                                il.Emit(OpCodes.Ldarg_0);                    // obj, obj, fieldname, converter?,    rdr
                                il.Emit(OpCodes.Ldc_I4, i);                    // obj, obj, fieldname, converter?,  rdr,i
                                il.Emit(OpCodes.Callvirt, fnGetValue);        // obj, obj, fieldname, converter?,  value
                                // Convert DBNull to null
                                il.Emit(OpCodes.Dup);                        // obj, obj, fieldname, converter?,  value, value
                                il.Emit(OpCodes.Isinst, typeof(DBNull));    // obj, obj, fieldname, converter?,  value, (value or null)
                                var lblNotNull = il.DefineLabel();
                                il.Emit(OpCodes.Brfalse_S, lblNotNull);        // obj, obj, fieldname, converter?,  value
                                il.Emit(OpCodes.Pop);                        // obj, obj, fieldname, converter?
                                if (converter != null)
                                    il.Emit(OpCodes.Pop);                    // obj, obj, fieldname, 
                                il.Emit(OpCodes.Ldnull);                    // obj, obj, fieldname, null
                                if (converter != null)
                                {
                                    var lblReady = il.DefineLabel();
                                    il.Emit(OpCodes.Br_S, lblReady);
                                    il.MarkLabel(lblNotNull);
                                    il.Emit(OpCodes.Callvirt, fnInvoke);
                                    il.MarkLabel(lblReady);
                                }
                                else
                                {
                                    il.MarkLabel(lblNotNull);
                                }
                                il.Emit(OpCodes.Callvirt, fnAdd);
                            }
                        }
                        else
    #endif
                            if (type.IsValueType || type == typeof(string) || type == typeof(byte[]))
                        {
                            // Do we need to install a converter?
                            var srcType = r.GetFieldType(0);
                            var converter = GetConverter(ForceDateTimesToUtc, null, srcType, type);
    
                            // "if (!rdr.IsDBNull(i))"
                            il.Emit(OpCodes.Ldarg_0);                                       // rdr
                            il.Emit(OpCodes.Ldc_I4_0);                                      // rdr,0
                            il.Emit(OpCodes.Callvirt, fnIsDBNull);                          // bool
                            var lblCont = il.DefineLabel();
                            il.Emit(OpCodes.Brfalse_S, lblCont);
                            il.Emit(OpCodes.Ldnull);                                        // null
                            var lblFin = il.DefineLabel();
                            il.Emit(OpCodes.Br_S, lblFin);
    
                            il.MarkLabel(lblCont);
    
                            // Setup stack for call to converter
                            AddConverterToStack(il, converter);
    
                            il.Emit(OpCodes.Ldarg_0);                                       // rdr
                            il.Emit(OpCodes.Ldc_I4_0);                                      // rdr,0
                            il.Emit(OpCodes.Callvirt, fnGetValue);                          // value
    
                            // Call the converter
                            if (converter != null)
                                il.Emit(OpCodes.Callvirt, fnInvoke);
    
                            il.MarkLabel(lblFin);
                            il.Emit(OpCodes.Unbox_Any, type);                               // value converted
                        }
                        else
                        {
                            // var poco=new T()
                            il.Emit(OpCodes.Newobj, type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[0], null));
    
                            // Enumerate all fields generating a set assignment for the column
                            for (int i = firstColumn; i < firstColumn + countColumns; i++)
                            {
                                // Get the PocoColumn for this db column, ignore if not known
                                PocoColumn pc;
                                if (!Columns.TryGetValue(r.GetName(i), out pc))
                                    continue;
    
                                // Get the source type for this column
                                var srcType = r.GetFieldType(i);
                                var dstType = pc.PropertyInfo.PropertyType;
    
                                // "if (!rdr.IsDBNull(i))"
                                il.Emit(OpCodes.Ldarg_0);                                       // poco,rdr
                                il.Emit(OpCodes.Ldc_I4, i);                                     // poco,rdr,i
                                il.Emit(OpCodes.Callvirt, fnIsDBNull);                          // poco,bool
                                var lblNext = il.DefineLabel();
                                il.Emit(OpCodes.Brtrue_S, lblNext);                             // poco
    
                                il.Emit(OpCodes.Dup);                                           // poco,poco
    
                                // Do we need to install a converter?
                                var converter = GetConverter(ForceDateTimesToUtc, pc, srcType, dstType);
    
                                // Fast
                                bool Handled = false;
                                if (converter == null)
                                {
                                    var valuegetter = typeof(IDataRecord).GetMethod("Get" + srcType.Name, new Type[] { typeof(int) });
                                    if (valuegetter != null
                                            && valuegetter.ReturnType == srcType
                                            && (valuegetter.ReturnType == dstType || valuegetter.ReturnType == Nullable.GetUnderlyingType(dstType)))
                                    {
                                        il.Emit(OpCodes.Ldarg_0);                                       // *,rdr
                                        il.Emit(OpCodes.Ldc_I4, i);                                     // *,rdr,i
                                        il.Emit(OpCodes.Callvirt, valuegetter);                         // *,value
    
                                        // Convert to Nullable
                                        if (Nullable.GetUnderlyingType(dstType) != null)
                                        {
                                            il.Emit(OpCodes.Newobj, dstType.GetConstructor(new Type[] { Nullable.GetUnderlyingType(dstType) }));
                                        }
    
                                        il.Emit(OpCodes.Callvirt, pc.PropertyInfo.GetSetMethod(true));      // poco
                                        Handled = true;
                                    }
                                }
    
                                // Not so fast
                                if (!Handled)
                                {
                                    // Setup stack for call to converter
                                    AddConverterToStack(il, converter);
    
                                    // "value = rdr.GetValue(i)"
                                    il.Emit(OpCodes.Ldarg_0);                                       // *,rdr
                                    il.Emit(OpCodes.Ldc_I4, i);                                     // *,rdr,i
                                    il.Emit(OpCodes.Callvirt, fnGetValue);                          // *,value
    
                                    // Call the converter
                                    if (converter != null)
                                        il.Emit(OpCodes.Callvirt, fnInvoke);
    
                                    // Assign it
                                    il.Emit(OpCodes.Unbox_Any, pc.PropertyInfo.PropertyType);       // poco,poco,value
                                    il.Emit(OpCodes.Callvirt, pc.PropertyInfo.GetSetMethod(true));      // poco
                                }
    
                                il.MarkLabel(lblNext);
                            }
    
                            var fnOnLoaded = RecurseInheritedTypes<MethodInfo>(type, (x) => x.GetMethod("OnLoaded", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[0], null));
                            if (fnOnLoaded != null)
                            {
                                il.Emit(OpCodes.Dup);
                                il.Emit(OpCodes.Callvirt, fnOnLoaded);
                            }
                        }
    
                        il.Emit(OpCodes.Ret);
    
                        // Cache it, return it
                        var del = m.CreateDelegate(Expression.GetFuncType(typeof(IDataReader), type));
                        PocoFactories.Add(key, del);
                        return del;
                    }
                    finally
                    {
                        RWLock.ExitWriteLock();
                    }
                }
    
                private static void AddConverterToStack(ILGenerator il, Func<object, object> converter)
                {
                    if (converter != null)
                    {
    
                        int converterIndex = m_Converters.Count;
                        m_Converters.Add(converter);
    
                        il.Emit(OpCodes.Ldsfld, fldConverters);
                        il.Emit(OpCodes.Ldc_I4, converterIndex);
                        il.Emit(OpCodes.Callvirt, fnListGetItem);                    // Converter
                    }
                }
    
                private static Func<object, object> GetConverter(bool forceDateTimesToUtc, PocoColumn pc, Type srcType, Type dstType)
                {
                    Func<object, object> converter = null;
    
    
                    if (Database.Mapper != null)
                    {
                        if (pc != null)
                        {
                            converter = Database.Mapper.GetFromDbConverter(pc.PropertyInfo, srcType);
                        }
                        else
                        {
                            var m2 = Database.Mapper as IMapper2;
                            if (m2 != null)
                            {
                                converter = m2.GetFromDbConverter(dstType, srcType);
                            }
                        }
                    }
    
                    // Standard DateTime->Utc mapper
                    if (forceDateTimesToUtc && converter == null && srcType == typeof(DateTime) && (dstType == typeof(DateTime) || dstType == typeof(DateTime?)))
                    {
                        converter = delegate (object src) { return new DateTime(((DateTime)src).Ticks, DateTimeKind.Utc); };
                    }
    
                    // Forced type conversion including integral types -> enum
                    if (converter == null)
                    {
                        if (dstType.IsEnum && IsIntegralType(srcType))
                        {
                            if (srcType != typeof(int))
                            {
                                converter = delegate (object src) { return Convert.ChangeType(src, typeof(int), null); };
                            }
                        }
                        else if (!dstType.IsAssignableFrom(srcType))
                        {
                            converter = delegate (object src) { return Convert.ChangeType(src, dstType, null); };
                        }
                    }
                    return converter;
                }
    
    
                static T RecurseInheritedTypes<T>(Type t, Func<Type, T> cb)
                {
                    while (t != null)
                    {
                        T info = cb(t);
                        if (info != null)
                            return info;
                        t = t.BaseType;
                    }
                    return default(T);
                }
                static Dictionary<Type, PocoData> m_PocoDatas = new Dictionary<Type, PocoData>();
                static List<Func<object, object>> m_Converters = new List<Func<object, object>>();
                static MethodInfo fnGetValue = typeof(IDataRecord).GetMethod("GetValue", new Type[] { typeof(int) });
                static MethodInfo fnIsDBNull = typeof(IDataRecord).GetMethod("IsDBNull");
                static FieldInfo fldConverters = typeof(PocoData).GetField("m_Converters", BindingFlags.Static | BindingFlags.GetField | BindingFlags.NonPublic);
                static MethodInfo fnListGetItem = typeof(List<Func<object, object>>).GetProperty("Item").GetGetMethod();
                static MethodInfo fnInvoke = typeof(Func<object, object>).GetMethod("Invoke");
                public Type type;
                public string[] QueryColumns { get; private set; }
                public TableInfo TableInfo { get; private set; }
                public Dictionary<string, PocoColumn> Columns { get; private set; }
                Dictionary<string, Delegate> PocoFactories = new Dictionary<string, Delegate>();
            }
            // Member variables
            string _connectionString;
            string _providerName;
            DbProviderFactory _factory;
            IDbConnection _sharedConnection;
            IDbTransaction _transaction;
            int _sharedConnectionDepth;
            int _transactionDepth;
            bool _transactionCancelled;
            string _lastSql;
            object[] _lastArgs;
            string _paramPrefix = "@";
    
            /// <summary>
            /// 缓存字典
            /// </summary>
            private static Dictionary<string, string> map = new Dictionary<string, string>();
    
            /// <summary>
            /// 防并发
            /// </summary>
            private static object lockobj = new object();
    
            /// <summary>
            /// 获取实体属性对应的列名
            /// </summary>
            /// <typeparam name="TValue">
            /// 列类型
            /// </typeparam>
            /// <param name="columnExpression">
            /// 列Expression.
            /// </param>
            /// <returns>
            /// 列名
            /// </returns>
            public static string GetTableColumn<T>(Expression<Func<T, object>> columnExpression)
            {
                var body = columnExpression.Body.ToString();
                if (body.Contains("ToString()"))
                {
                    body = body.Remove(body.IndexOf(".ToString()"), ".ToString()".Length);
                }
                return GetTableColumn<T>(body.Split('.').Last().Replace(")", ""));
            }
    
            /// <summary>
            /// 获取实体属性对应的列名
            /// </summary>
            /// <param name="property">
            /// 列Expression.
            /// </param>
            /// <returns>
            /// 列名
            /// </returns>
            public static string GetTableColumn<T>(string property)
            {
                var key = typeof(T) + "_" + property;
                string column = null;
                if (map.ContainsKey(key))
                {
                    column = map[key];
                }
                if (column != null)
                {
                    return column;
                }
                lock (lockobj)
                {
                    var list = from c in typeof(T).GetProperties() where c.Name == property select c;
                    var keyColumns = (from d in (from c in typeof(T).GetProperties() where c.Name == property select c) select d.GetCustomAttributes(typeof(ColumnAttribute), false)).First();
                    column = keyColumns.Count() > 0 ? ((ColumnAttribute)(keyColumns[0])).Name : property;
                    if (map.ContainsKey(key))
                    {
                        map.Remove(key);
                    }
                    map.Add(key, column);
                    return column;
                }
            }
    
            /// <summary>
            /// 获取实体属性对应的列名
            /// </summary>
            /// <param name="property">
            /// 列Expression.
            /// </param>
            /// <returns>
            /// 列名
            /// </returns>
            public static string GetTableColumn<T>(PropertyInfo property)
            {
                return GetTableColumn<T>(property.Name);
            }
    
            /// <summary>
            /// 获取Table的key
            /// </summary>
            /// <returns>关键列名</returns>
            public static string GetTableName<T>()
            {
                var key = typeof(T) + "_TableName";
                string tableName = null;
                if (map.ContainsKey(key))
                {
                    tableName = map[key];
                }
                if (tableName != null)
                {
                    return tableName;
                }
                lock (lockobj)
                {
                    tableName = ((TableAttribute)typeof(T).GetCustomAttributes(typeof(TableAttribute), false)[0]).Value;
                    if (map.ContainsKey(key)) map.Remove(key);
                    map.Add(key, tableName);
                    return tableName;
                }
            }
        }
    
        /// <summary>
        /// 事务
        /// </summary>
        public class Transaction : IDisposable
        {
            public Transaction(Database db)
            {
                _db = db;
                _db.BeginTransaction();
            }
    
            public virtual void Complete()
            {
                _db.CompleteTransaction();
                _db = null;
            }
    
            public void Dispose()
            {
                if (_db != null)
                    _db.AbortTransaction();
            }
    
            Database _db;
        }
    
        public class Sql
        {
            public Sql()
            {
            }
    
            public Sql(string sql, params object[] args)
            {
                _sql = sql;
                _args = args;
            }
    
            public static Sql Builder
            {
                get { return new Sql(); }
            }
    
            string _sql;
            object[] _args;
            Sql _rhs;
            string _sqlFinal;
            object[] _argsFinal;
    
            private void Build()
            {
                // already built?
                if (_sqlFinal != null)
                    return;
    
                // Build it
                var sb = new StringBuilder();
                var args = new List<object>();
                Build(sb, args, null);
                _sqlFinal = sb.ToString();
                _argsFinal = args.ToArray();
            }
    
            public string SQL
            {
                get
                {
                    Build();
                    return _sqlFinal;
                }
            }
    
            public object[] Arguments
            {
                get
                {
                    Build();
                    return _argsFinal;
                }
            }
    
            public Sql Append(Sql sql)
            {
                if (_rhs != null)
                    _rhs.Append(sql);
                else
                    _rhs = sql;
    
                return this;
            }
    
            public Sql Append(string sql, params object[] args)
            {
                return Append(new Sql(sql, args));
            }
    
            static bool Is(Sql sql, string sqltype)
            {
                return sql != null && sql._sql != null && sql._sql.StartsWith(sqltype, StringComparison.InvariantCultureIgnoreCase);
            }
    
            private void Build(StringBuilder sb, List<object> args, Sql lhs)
            {
                if (!String.IsNullOrEmpty(_sql))
                {
                    if (sb.Length > 0)
                    {
                        sb.Append("
    ");
                    }
    
                    var sql = Database.ProcessParams(_sql, _args, args);
    
                    if (Is(lhs, "WHERE ") && Is(this, "WHERE "))
                        sql = "AND " + sql.Substring(6);
                    if (Is(lhs, "ORDER BY ") && Is(this, "ORDER BY "))
                        sql = ", " + sql.Substring(9);
    
                    sb.Append(sql);
                }
    
                if (_rhs != null)
                    _rhs.Build(sb, args, this);
            }
    
            public Sql Where(string sql, params object[] args)
            {
                return Append(new Sql("WHERE (" + sql + ")", args));
            }
    
            public Sql OrderBy(params object[] columns)
            {
                return Append(new Sql("ORDER BY " + String.Join(", ", (from x in columns select x.ToString()).ToArray())));
            }
    
            public Sql Select(params object[] columns)
            {
                return Append(new Sql("SELECT " + String.Join(", ", (from x in columns select x.ToString()).ToArray())));
            }
    
            public Sql From(params object[] tables)
            {
                return Append(new Sql("FROM " + String.Join(", ", (from x in tables select x.ToString()).ToArray())));
            }
    
            public Sql GroupBy(params object[] columns)
            {
                return Append(new Sql("GROUP BY " + String.Join(", ", (from x in columns select x.ToString()).ToArray())));
            }
    
            private SqlJoinClause Join(string JoinType, string table)
            {
                return new SqlJoinClause(Append(new Sql(JoinType + table)));
            }
    
            public SqlJoinClause InnerJoin(string table) { return Join("INNER JOIN ", table); }
            public SqlJoinClause LeftJoin(string table) { return Join("LEFT JOIN ", table); }
    
            public class SqlJoinClause
            {
                private readonly Sql _sql;
    
                public SqlJoinClause(Sql sql)
                {
                    _sql = sql;
                }
    
                public Sql On(string onClause, params object[] args)
                {
                    return _sql.Append("ON " + onClause, args);
                }
            }
    
    
        }
    
        public static class StringExtension
        {
            /// <summary>
            /// 在SQL语句后面追加列
            /// </summary>
            /// <param name="sql">
            /// 需追加列SQL语句
            /// </param>
            /// <param name="colomns">
            /// 实体字段
            /// </param>
            /// <returns>
            /// 添加列后的SQL
            /// </returns>
            public static string AppendColomns<TModel>(this string sql,
                 params Expression<Func<TModel, object>>[] colomns)
            {
                string[] cols = (from c in colomns select Database.GetTableColumn(c)).ToArray();
                return string.Format(" {0} {1} ", sql, String.Join(",", cols));
            }
    
            /// <summary>
            /// 在SQL语句后面追加表名
            /// </summary>
            /// <param name="sql">
            /// 需追加表名SQL语句
            /// </param>
            /// <returns>
            /// 添加表名后的SQL
            /// </returns>
            public static string AppendTableName<TModel>(this string sql)
            {
                return string.Format(" {0} {1} ", sql, Database.GetTableName<TModel>());
            }
        }
    }

  • 相关阅读:
    2017 Multi-University Training Contest 2.Balala Power!(贪心)
    2017ICPCECIC C.A math problem(高次剩余)
    Atcoder 068E
    51nod 1385 凑数字(贪心+构造)
    cf round #418 div2 D. An overnight dance in discotheque(贪心)
    cf round #418 div2 C. An impassioned circulation of affection(暴力)
    cf round #424 div2 E. Cards Sorting(线段树)
    Atcoder 077E
    hdu 6162 Ch’s gift(树链剖分+主席树)
    Educational Codeforces Round 26 D. Round Subset(dp)
  • 原文地址:https://www.cnblogs.com/jiangqw/p/12121253.html
Copyright © 2011-2022 走看看