mirror of
				https://github.com/nsnail/FreeSql.git
				synced 2025-11-04 17:20:49 +08:00 
			
		
		
		
	- 增加 所有国产数据库支持 CustomMySql、CustomPostgreSQL、CustomOracle、CustomSqlServer 自定义适配;
This commit is contained in:
		@@ -0,0 +1,133 @@
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Data;
 | 
			
		||||
using System.Data.Common;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Custom.SqlServer
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    class CustomSqlServerDelete<T1> : Internal.CommonProvider.DeleteProvider<T1>
 | 
			
		||||
    {
 | 
			
		||||
        public CustomSqlServerDelete(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere)
 | 
			
		||||
            : base(orm, commonUtils, commonExpression, dywhere)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override List<T1> ExecuteDeleted()
 | 
			
		||||
        {
 | 
			
		||||
            var ret = new List<T1>();
 | 
			
		||||
            DbParameter[] dbParms = null;
 | 
			
		||||
            StringBuilder sbret = null;
 | 
			
		||||
            ToSqlFetch(sb =>
 | 
			
		||||
            {
 | 
			
		||||
                if (dbParms == null)
 | 
			
		||||
                {
 | 
			
		||||
                    dbParms = _params.ToArray();
 | 
			
		||||
                    sbret = new StringBuilder();
 | 
			
		||||
                    sbret.Append(" OUTPUT ");
 | 
			
		||||
 | 
			
		||||
                    var colidx = 0;
 | 
			
		||||
                    foreach (var col in _table.Columns.Values)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (colidx > 0) sbret.Append(", ");
 | 
			
		||||
                        sbret.Append(_commonUtils.RereadColumn(col, $"DELETED.{_commonUtils.QuoteSqlName(col.Attribute.Name)}")).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
 | 
			
		||||
                        ++colidx;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                var sql = sb.ToString();
 | 
			
		||||
                var validx = sql.IndexOf(" WHERE ");
 | 
			
		||||
                if (validx == -1) throw new ArgumentException(CoreStrings.S_NotFound_Name("WHERE"));
 | 
			
		||||
                sql = sb.Clear().Append(sql.Substring(0, validx))
 | 
			
		||||
                    .Append(sbret)
 | 
			
		||||
                    .Append(sql.Substring(validx)).ToString();
 | 
			
		||||
 | 
			
		||||
                var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Delete, sql, dbParms);
 | 
			
		||||
                _orm.Aop.CurdBeforeHandler?.Invoke(this, before);
 | 
			
		||||
 | 
			
		||||
                Exception exception = null;
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    ret.AddRange(_orm.Ado.Query<T1>(_table.TypeLazy ?? _table.Type, _connection, _transaction, CommandType.Text, sql, _commandTimeout, dbParms));
 | 
			
		||||
                }
 | 
			
		||||
                catch (Exception ex)
 | 
			
		||||
                {
 | 
			
		||||
                    exception = ex;
 | 
			
		||||
                    throw ex;
 | 
			
		||||
                }
 | 
			
		||||
                finally
 | 
			
		||||
                {
 | 
			
		||||
                    var after = new Aop.CurdAfterEventArgs(before, exception, ret);
 | 
			
		||||
                    _orm.Aop.CurdAfterHandler?.Invoke(this, after);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            if (dbParms != null)
 | 
			
		||||
            {
 | 
			
		||||
                this.ClearData();
 | 
			
		||||
                sbret.Clear();
 | 
			
		||||
            }
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
#if net40
 | 
			
		||||
#else
 | 
			
		||||
        async public override Task<List<T1>> ExecuteDeletedAsync(CancellationToken cancellationToken = default)
 | 
			
		||||
        {
 | 
			
		||||
            var ret = new List<T1>();
 | 
			
		||||
            DbParameter[] dbParms = null;
 | 
			
		||||
            StringBuilder sbret = null;
 | 
			
		||||
            await ToSqlFetchAsync(async sb =>
 | 
			
		||||
            {
 | 
			
		||||
                if (dbParms == null)
 | 
			
		||||
                {
 | 
			
		||||
                    dbParms = _params.ToArray();
 | 
			
		||||
                    sbret = new StringBuilder();
 | 
			
		||||
                    sbret.Append(" OUTPUT ");
 | 
			
		||||
 | 
			
		||||
                    var colidx = 0;
 | 
			
		||||
                    foreach (var col in _table.Columns.Values)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (colidx > 0) sbret.Append(", ");
 | 
			
		||||
                        sbret.Append(_commonUtils.RereadColumn(col, $"DELETED.{_commonUtils.QuoteSqlName(col.Attribute.Name)}")).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
 | 
			
		||||
                        ++colidx;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                var sql = sb.ToString();
 | 
			
		||||
                var validx = sql.IndexOf(" WHERE ");
 | 
			
		||||
                if (validx == -1) throw new ArgumentException(CoreStrings.S_NotFound_Name("WHERE"));
 | 
			
		||||
                sql = sb.Clear().Append(sql.Substring(0, validx))
 | 
			
		||||
                    .Append(sbret)
 | 
			
		||||
                    .Append(sql.Substring(validx)).ToString();
 | 
			
		||||
 | 
			
		||||
                var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Delete, sql, dbParms);
 | 
			
		||||
                _orm.Aop.CurdBeforeHandler?.Invoke(this, before);
 | 
			
		||||
 | 
			
		||||
                Exception exception = null;
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    ret.AddRange(await _orm.Ado.QueryAsync<T1>(_table.TypeLazy ?? _table.Type, _connection, _transaction, CommandType.Text, sql, _commandTimeout, dbParms, cancellationToken));
 | 
			
		||||
                }
 | 
			
		||||
                catch (Exception ex)
 | 
			
		||||
                {
 | 
			
		||||
                    exception = ex;
 | 
			
		||||
                    throw ex;
 | 
			
		||||
                }
 | 
			
		||||
                finally
 | 
			
		||||
                {
 | 
			
		||||
                    var after = new Aop.CurdAfterEventArgs(before, exception, ret);
 | 
			
		||||
                    _orm.Aop.CurdAfterHandler?.Invoke(this, after);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            if (dbParms != null)
 | 
			
		||||
            {
 | 
			
		||||
                this.ClearData();
 | 
			
		||||
                sbret.Clear();
 | 
			
		||||
            }
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,191 @@
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Data;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Custom.SqlServer
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    class CustomSqlServerInsert<T1> : Internal.CommonProvider.InsertProvider<T1> where T1 : class
 | 
			
		||||
    {
 | 
			
		||||
        public CustomSqlServerInsert(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression)
 | 
			
		||||
            : base(orm, commonUtils, commonExpression)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override int ExecuteAffrows() => base.SplitExecuteAffrows(_batchValuesLimit > 0 ? _batchValuesLimit : 1000, _batchParameterLimit > 0 ? _batchParameterLimit : 2100);
 | 
			
		||||
        public override long ExecuteIdentity() => base.SplitExecuteIdentity(_batchValuesLimit > 0 ? _batchValuesLimit : 1000, _batchParameterLimit > 0 ? _batchParameterLimit : 2100);
 | 
			
		||||
        public override List<T1> ExecuteInserted() => base.SplitExecuteInserted(_batchValuesLimit > 0 ? _batchValuesLimit : 1000, _batchParameterLimit > 0 ? _batchParameterLimit : 2100);
 | 
			
		||||
 | 
			
		||||
        public override string ToSql()
 | 
			
		||||
        {
 | 
			
		||||
            var versionGreaterThan10 = (_commonUtils as CustomSqlServerUtils).ServerVersion > 10;
 | 
			
		||||
            return this.ToSqlValuesOrSelectUnionAll(versionGreaterThan10);
 | 
			
		||||
        }
 | 
			
		||||
        protected override long RawExecuteIdentity()
 | 
			
		||||
        {
 | 
			
		||||
            var sql = this.ToSql();
 | 
			
		||||
            if (string.IsNullOrEmpty(sql)) return 0;
 | 
			
		||||
 | 
			
		||||
            sql = string.Concat(sql, "; SELECT SCOPE_IDENTITY();");
 | 
			
		||||
            var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, sql, _params);
 | 
			
		||||
            _orm.Aop.CurdBeforeHandler?.Invoke(this, before);
 | 
			
		||||
            long ret = 0;
 | 
			
		||||
            Exception exception = null;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                long.TryParse(string.Concat(_orm.Ado.ExecuteScalar(_connection, _transaction, CommandType.Text, sql, _commandTimeout, _params)), out ret);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                exception = ex;
 | 
			
		||||
                throw ex;
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
                var after = new Aop.CurdAfterEventArgs(before, exception, ret);
 | 
			
		||||
                _orm.Aop.CurdAfterHandler?.Invoke(this, after);
 | 
			
		||||
            }
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
        protected override List<T1> RawExecuteInserted()
 | 
			
		||||
        {
 | 
			
		||||
            var sql = this.ToSql();
 | 
			
		||||
            if (string.IsNullOrEmpty(sql)) return new List<T1>();
 | 
			
		||||
 | 
			
		||||
            var sb = new StringBuilder();
 | 
			
		||||
            sb.Append(" OUTPUT ");
 | 
			
		||||
            var colidx = 0;
 | 
			
		||||
            foreach (var col in _table.Columns.Values)
 | 
			
		||||
            {
 | 
			
		||||
                if (colidx > 0) sb.Append(", ");
 | 
			
		||||
                sb.Append(_commonUtils.RereadColumn(col, $"INSERTED.{_commonUtils.QuoteSqlName(col.Attribute.Name)}")).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
 | 
			
		||||
                ++colidx;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if ((_commonUtils as CustomSqlServerUtils).ServerVersion > 10)
 | 
			
		||||
            {
 | 
			
		||||
                var validx = sql.IndexOf(") VALUES");
 | 
			
		||||
                if (validx == -1) throw new ArgumentException(CoreStrings.S_NotFound_Name("VALUES"));
 | 
			
		||||
                sb.Insert(0, sql.Substring(0, validx + 1));
 | 
			
		||||
                sb.Append(sql.Substring(validx + 1));
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                var validx = sql.IndexOf(") SELECT ");
 | 
			
		||||
                if (validx == -1) throw new ArgumentException(CoreStrings.S_NotFound_Name("SELECT"));
 | 
			
		||||
                sb.Insert(0, sql.Substring(0, validx + 1));
 | 
			
		||||
                sb.Append(sql.Substring(validx + 1));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            sql = sb.ToString();
 | 
			
		||||
            var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, sql, _params);
 | 
			
		||||
            _orm.Aop.CurdBeforeHandler?.Invoke(this, before);
 | 
			
		||||
            var ret = new List<T1>();
 | 
			
		||||
            Exception exception = null;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                ret = _orm.Ado.Query<T1>(_table.TypeLazy ?? _table.Type, _connection, _transaction, CommandType.Text, sql, _commandTimeout, _params);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                exception = ex;
 | 
			
		||||
                throw ex;
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
                var after = new Aop.CurdAfterEventArgs(before, exception, ret);
 | 
			
		||||
                _orm.Aop.CurdAfterHandler?.Invoke(this, after);
 | 
			
		||||
            }
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
#if net40
 | 
			
		||||
#else
 | 
			
		||||
        public override Task<int> ExecuteAffrowsAsync(CancellationToken cancellationToken = default) => base.SplitExecuteAffrowsAsync(_batchValuesLimit > 0 ? _batchValuesLimit : 1000, _batchParameterLimit > 0 ? _batchParameterLimit : 2100, cancellationToken);
 | 
			
		||||
        public override Task<long> ExecuteIdentityAsync(CancellationToken cancellationToken = default) => base.SplitExecuteIdentityAsync(_batchValuesLimit > 0 ? _batchValuesLimit : 1000, _batchParameterLimit > 0 ? _batchParameterLimit : 2100, cancellationToken);
 | 
			
		||||
        public override Task<List<T1>> ExecuteInsertedAsync(CancellationToken cancellationToken = default) => base.SplitExecuteInsertedAsync(_batchValuesLimit > 0 ? _batchValuesLimit : 1000, _batchParameterLimit > 0 ? _batchParameterLimit : 2100, cancellationToken);
 | 
			
		||||
 | 
			
		||||
        async protected override Task<long> RawExecuteIdentityAsync(CancellationToken cancellationToken = default)
 | 
			
		||||
        {
 | 
			
		||||
            var sql = this.ToSql();
 | 
			
		||||
            if (string.IsNullOrEmpty(sql)) return 0;
 | 
			
		||||
 | 
			
		||||
            sql = string.Concat(sql, "; SELECT SCOPE_IDENTITY();");
 | 
			
		||||
            var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, sql, _params);
 | 
			
		||||
            _orm.Aop.CurdBeforeHandler?.Invoke(this, before);
 | 
			
		||||
            long ret = 0;
 | 
			
		||||
            Exception exception = null;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                long.TryParse(string.Concat(await _orm.Ado.ExecuteScalarAsync(_connection, _transaction, CommandType.Text, sql, _commandTimeout, _params, cancellationToken)), out ret);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                exception = ex;
 | 
			
		||||
                throw ex;
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
                var after = new Aop.CurdAfterEventArgs(before, exception, ret);
 | 
			
		||||
                _orm.Aop.CurdAfterHandler?.Invoke(this, after);
 | 
			
		||||
            }
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
        async protected override Task<List<T1>> RawExecuteInsertedAsync(CancellationToken cancellationToken = default)
 | 
			
		||||
        {
 | 
			
		||||
            var sql = this.ToSql();
 | 
			
		||||
            if (string.IsNullOrEmpty(sql)) return new List<T1>();
 | 
			
		||||
 | 
			
		||||
            var sb = new StringBuilder();
 | 
			
		||||
            sb.Append(" OUTPUT ");
 | 
			
		||||
            var colidx = 0;
 | 
			
		||||
            foreach (var col in _table.Columns.Values)
 | 
			
		||||
            {
 | 
			
		||||
                if (colidx > 0) sb.Append(", ");
 | 
			
		||||
                sb.Append(_commonUtils.RereadColumn(col, $"INSERTED.{_commonUtils.QuoteSqlName(col.Attribute.Name)}")).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
 | 
			
		||||
                ++colidx;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if ((_commonUtils as CustomSqlServerUtils).ServerVersion > 10)
 | 
			
		||||
            {
 | 
			
		||||
                var validx = sql.IndexOf(") VALUES");
 | 
			
		||||
                if (validx == -1) throw new ArgumentException(CoreStrings.S_NotFound_Name("VALUES"));
 | 
			
		||||
                sb.Insert(0, sql.Substring(0, validx + 1));
 | 
			
		||||
                sb.Append(sql.Substring(validx + 1));
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                var validx = sql.IndexOf(") SELECT ");
 | 
			
		||||
                if (validx == -1) throw new ArgumentException(CoreStrings.S_NotFound_Name("SELECT"));
 | 
			
		||||
                sb.Insert(0, sql.Substring(0, validx + 1));
 | 
			
		||||
                sb.Append(sql.Substring(validx + 1));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            sql = sb.ToString();
 | 
			
		||||
            var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, sql, _params);
 | 
			
		||||
            _orm.Aop.CurdBeforeHandler?.Invoke(this, before);
 | 
			
		||||
            var ret = new List<T1>();
 | 
			
		||||
            Exception exception = null;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                ret = await _orm.Ado.QueryAsync<T1>(_table.TypeLazy ?? _table.Type, _connection, _transaction, CommandType.Text, sql, _commandTimeout, _params, cancellationToken);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                exception = ex;
 | 
			
		||||
                throw ex;
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
                var after = new Aop.CurdAfterEventArgs(before, exception, ret);
 | 
			
		||||
                _orm.Aop.CurdAfterHandler?.Invoke(this, after);
 | 
			
		||||
            }
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,78 @@
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Data.Common;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Custom.SqlServer
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    class CustomSqlServerInsertOrUpdate<T1> : Internal.CommonProvider.InsertOrUpdateProvider<T1> where T1 : class
 | 
			
		||||
    {
 | 
			
		||||
        public CustomSqlServerInsertOrUpdate(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression)
 | 
			
		||||
            : base(orm, commonUtils, commonExpression)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override string ToSql()
 | 
			
		||||
        {
 | 
			
		||||
            var dbParams = new List<DbParameter>();
 | 
			
		||||
            if (_sourceSql != null) return getMergeSql(null);
 | 
			
		||||
            if (_source?.Any() != true) return null;
 | 
			
		||||
 | 
			
		||||
            var sqls = new string[2];
 | 
			
		||||
            var ds = SplitSourceByIdentityValueIsNull(_source);
 | 
			
		||||
            if (ds.Item1.Any()) sqls[0] = string.Join("\r\n\r\n;\r\n\r\n", ds.Item1.Select(a => getMergeSql(a)));
 | 
			
		||||
            if (ds.Item2.Any()) sqls[1] = string.Join("\r\n\r\n;\r\n\r\n", ds.Item2.Select(a => getInsertSql(a)));
 | 
			
		||||
            _params = dbParams.ToArray();
 | 
			
		||||
            if (ds.Item2.Any() == false) return sqls[0];
 | 
			
		||||
            if (ds.Item1.Any() == false) return sqls[1];
 | 
			
		||||
            return string.Join("\r\n\r\n;\r\n\r\n", sqls);
 | 
			
		||||
 | 
			
		||||
            string getMergeSql(List<T1> data)
 | 
			
		||||
            {
 | 
			
		||||
                if (_tempPrimarys.Any() == false) throw new Exception(CoreStrings.InsertOrUpdate_Must_Primary_Key(_table.CsName));
 | 
			
		||||
 | 
			
		||||
                var sb = new StringBuilder();
 | 
			
		||||
                if (IdentityColumn != null) sb.Append("SET IDENTITY_INSERT ").Append(_commonUtils.QuoteSqlName(TableRuleInvoke())).Append(" ON;\r\n");
 | 
			
		||||
                sb.Append("MERGE INTO ").Append(_commonUtils.QuoteSqlName(TableRuleInvoke())).Append(" t1 \r\nUSING (");
 | 
			
		||||
                WriteSourceSelectUnionAll(data, sb, dbParams);
 | 
			
		||||
                sb.Append(" ) t2 ON (").Append(string.Join(" AND ", _tempPrimarys.Select(a => $"t1.{_commonUtils.QuoteSqlName(a.Attribute.Name)} = t2.{_commonUtils.QuoteSqlName(a.Attribute.Name)}"))).Append(") \r\n");
 | 
			
		||||
 | 
			
		||||
                var cols = _table.Columns.Values.Where(a => _tempPrimarys.Contains(a) == false && a.Attribute.CanUpdate == true && _updateIgnore.ContainsKey(a.Attribute.Name) == false);
 | 
			
		||||
                if (_doNothing == false && cols.Any())
 | 
			
		||||
                    sb.Append("WHEN MATCHED THEN \r\n")
 | 
			
		||||
                        .Append("  update set ").Append(string.Join(", ", cols.Select(a =>
 | 
			
		||||
                            a.Attribute.IsVersion && a.Attribute.MapType != typeof(byte[]) ?
 | 
			
		||||
                            $"{_commonUtils.QuoteSqlName(a.Attribute.Name)} = t1.{_commonUtils.QuoteSqlName(a.Attribute.Name)} + 1" :
 | 
			
		||||
                            $"{_commonUtils.QuoteSqlName(a.Attribute.Name)} = t2.{_commonUtils.QuoteSqlName(a.Attribute.Name)}"
 | 
			
		||||
                            ))).Append(" \r\n");
 | 
			
		||||
 | 
			
		||||
                cols = _table.Columns.Values.Where(a => a.Attribute.CanInsert == true);
 | 
			
		||||
                if (cols.Any())
 | 
			
		||||
                    sb.Append("WHEN NOT MATCHED THEN \r\n")
 | 
			
		||||
                        .Append("  insert (").Append(string.Join(", ", cols.Select(a => _commonUtils.QuoteSqlName(a.Attribute.Name)))).Append(") \r\n")
 | 
			
		||||
                        .Append("  values (").Append(string.Join(", ", cols.Select(a => $"t2.{_commonUtils.QuoteSqlName(a.Attribute.Name)}"))).Append(");");
 | 
			
		||||
 | 
			
		||||
                if (IdentityColumn != null) sb.Append(";\r\nSET IDENTITY_INSERT ").Append(_commonUtils.QuoteSqlName(TableRuleInvoke())).Append(" OFF;");
 | 
			
		||||
 | 
			
		||||
                return sb.ToString();
 | 
			
		||||
            }
 | 
			
		||||
            string getInsertSql(List<T1> data)
 | 
			
		||||
            {
 | 
			
		||||
                var insert = _orm.Insert<T1>()
 | 
			
		||||
                    .AsTable(_tableRule).AsType(_table.Type)
 | 
			
		||||
                    .WithConnection(_connection)
 | 
			
		||||
                    .WithTransaction(_transaction)
 | 
			
		||||
                    .NoneParameter(true) as Internal.CommonProvider.InsertProvider<T1>;
 | 
			
		||||
                insert._source = data;
 | 
			
		||||
                insert._table = _table;
 | 
			
		||||
                var sql = insert.ToSql();
 | 
			
		||||
                if (string.IsNullOrEmpty(sql)) return null;
 | 
			
		||||
                if (insert._params?.Any() == true) dbParams.AddRange(insert._params);
 | 
			
		||||
                return sql;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,369 @@
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using FreeSql.Internal.Model;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Linq.Expressions;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Custom.SqlServer
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    class CustomSqlServerSelect<T1> : FreeSql.Internal.CommonProvider.Select1Provider<T1>
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        internal static string ToSqlStatic(CommonUtils _commonUtils, CommonExpression _commonExpression, string _select, bool _distinct, string field, StringBuilder _join, StringBuilder _where, string _groupby, string _having, string _orderby, int _skip, int _limit, List<SelectTableInfo> _tables, List<Dictionary<Type, string>> tbUnions, Func<Type, string, string> _aliasRule, string _tosqlAppendContent, List<GlobalFilter.Item> _whereGlobalFilter, IFreeSql _orm)
 | 
			
		||||
            => (_commonUtils as CustomSqlServerUtils).IsSelectRowNumber ?
 | 
			
		||||
            ToSqlStaticRowNumber(_commonUtils, _commonExpression, _select, _distinct, field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, tbUnions, _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm) :
 | 
			
		||||
            ToSqlStaticOffsetFetchNext(_commonUtils, _commonExpression, _select, _distinct, field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, tbUnions, _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
 | 
			
		||||
 | 
			
		||||
        #region SqlServer 2005 row_number
 | 
			
		||||
        internal static string ToSqlStaticRowNumber(CommonUtils _commonUtils, CommonExpression _commonExpression, string _select, bool _distinct, string field, StringBuilder _join, StringBuilder _where, string _groupby, string _having, string _orderby, int _skip, int _limit, List<SelectTableInfo> _tables, List<Dictionary<Type, string>> tbUnions, Func<Type, string, string> _aliasRule, string _tosqlAppendContent, List<GlobalFilter.Item> _whereGlobalFilter, IFreeSql _orm)
 | 
			
		||||
        {
 | 
			
		||||
            if (_orm.CodeFirst.IsAutoSyncStructure)
 | 
			
		||||
                _orm.CodeFirst.SyncStructure(_tables.Select(a => a.Table.Type).ToArray());
 | 
			
		||||
 | 
			
		||||
            if (_whereGlobalFilter.Any())
 | 
			
		||||
                foreach (var tb in _tables.Where(a => a.Type != SelectTableInfoType.Parent))
 | 
			
		||||
                    tb.Cascade = _commonExpression.GetWhereCascadeSql(tb, _whereGlobalFilter, true);
 | 
			
		||||
 | 
			
		||||
            var sb = new StringBuilder();
 | 
			
		||||
            var tbUnionsGt0 = tbUnions.Count > 1;
 | 
			
		||||
            for (var tbUnionsIdx = 0; tbUnionsIdx < tbUnions.Count; tbUnionsIdx++)
 | 
			
		||||
            {
 | 
			
		||||
                if (tbUnionsIdx > 0) sb.Append(" \r\n\r\nUNION ALL\r\n\r\n");
 | 
			
		||||
                if (tbUnionsGt0) sb.Append(_select).Append(" * from (");
 | 
			
		||||
                var tbUnion = tbUnions[tbUnionsIdx];
 | 
			
		||||
 | 
			
		||||
                var sbnav = new StringBuilder();
 | 
			
		||||
                sb.Append(_select);
 | 
			
		||||
                if (_distinct) sb.Append("DISTINCT ");
 | 
			
		||||
                //if (_limit > 0) sb.Append("TOP ").Append(_skip + _limit).Append(" "); //TOP 会引发 __rownum__ 无序的问题
 | 
			
		||||
                if (_skip <= 0 && _limit > 0) sb.Append("TOP ").Append(_limit).Append(" ");
 | 
			
		||||
                sb.Append(field);
 | 
			
		||||
 | 
			
		||||
                if (_limit > 0 || _skip > 0)
 | 
			
		||||
                {
 | 
			
		||||
                    if (string.IsNullOrEmpty(_orderby))
 | 
			
		||||
                    {
 | 
			
		||||
                        if (string.IsNullOrEmpty(_groupby))
 | 
			
		||||
                        {
 | 
			
		||||
                            var pktb = _tables.Where(a => a.Table.Primarys.Any()).FirstOrDefault();
 | 
			
		||||
                            if (pktb != null) _orderby = string.Concat(" \r\nORDER BY ", pktb.Alias, ".", _commonUtils.QuoteSqlName(pktb?.Table.Primarys.First().Attribute.Name));
 | 
			
		||||
                            else _orderby = string.Concat(" \r\nORDER BY ", _tables.First().Alias, ".", _commonUtils.QuoteSqlName(_tables.First().Table.Columns.First().Value.Attribute.Name));
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                            _orderby = _groupby.Replace("GROUP BY ", "ORDER BY ");
 | 
			
		||||
                    }
 | 
			
		||||
                    if (_skip > 0) // 注意这个判断,大于 0 才使用 ROW_NUMBER ,否则属于第一页直接使用 TOP
 | 
			
		||||
                        sb.Append(", ROW_NUMBER() OVER(").Append(_orderby.Trim('\r', '\n', ' ')).Append(") AS __rownum__");
 | 
			
		||||
                }
 | 
			
		||||
                sb.Append(" \r\nFROM ");
 | 
			
		||||
                var tbsjoin = _tables.Where(a => a.Type != SelectTableInfoType.From).ToArray();
 | 
			
		||||
                var tbsfrom = _tables.Where(a => a.Type == SelectTableInfoType.From).ToArray();
 | 
			
		||||
                for (var a = 0; a < tbsfrom.Length; a++)
 | 
			
		||||
                {
 | 
			
		||||
                    sb.Append(_commonUtils.QuoteSqlName(tbUnion[tbsfrom[a].Table.Type])).Append(" ").Append(_aliasRule?.Invoke(tbsfrom[a].Table.Type, tbsfrom[a].Alias) ?? tbsfrom[a].Alias);
 | 
			
		||||
                    if (tbsjoin.Length > 0)
 | 
			
		||||
                    {
 | 
			
		||||
                        //如果存在 join 查询,则处理 from t1, t2 改为 from t1 inner join t2 on 1 = 1
 | 
			
		||||
                        for (var b = 1; b < tbsfrom.Length; b++)
 | 
			
		||||
                        {
 | 
			
		||||
                            sb.Append(" \r\nLEFT JOIN ").Append(_commonUtils.QuoteSqlName(tbUnion[tbsfrom[b].Table.Type])).Append(" ").Append(_aliasRule?.Invoke(tbsfrom[b].Table.Type, tbsfrom[b].Alias) ?? tbsfrom[b].Alias);
 | 
			
		||||
 | 
			
		||||
                            if (string.IsNullOrEmpty(tbsfrom[b].NavigateCondition) && string.IsNullOrEmpty(tbsfrom[b].On) && string.IsNullOrEmpty(tbsfrom[b].Cascade)) sb.Append(" ON 1 = 1");
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                var onSql = tbsfrom[b].NavigateCondition ?? tbsfrom[b].On;
 | 
			
		||||
                                sb.Append(" ON ").Append(onSql);
 | 
			
		||||
                                if (string.IsNullOrEmpty(tbsfrom[b].Cascade) == false)
 | 
			
		||||
                                {
 | 
			
		||||
                                    if (string.IsNullOrEmpty(onSql)) sb.Append(tbsfrom[b].Cascade);
 | 
			
		||||
                                    else sb.Append(" AND ").Append(tbsfrom[b].Cascade);
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        if (!string.IsNullOrEmpty(tbsfrom[a].NavigateCondition)) sbnav.Append(" AND (").Append(tbsfrom[a].NavigateCondition).Append(")");
 | 
			
		||||
                        if (!string.IsNullOrEmpty(tbsfrom[a].On)) sbnav.Append(" AND (").Append(tbsfrom[a].On).Append(")");
 | 
			
		||||
                        if (a > 0 && !string.IsNullOrEmpty(tbsfrom[a].Cascade)) sbnav.Append(" AND ").Append(tbsfrom[a].Cascade);
 | 
			
		||||
                    }
 | 
			
		||||
                    if (a < tbsfrom.Length - 1) sb.Append(", ");
 | 
			
		||||
                }
 | 
			
		||||
                foreach (var tb in tbsjoin)
 | 
			
		||||
                {
 | 
			
		||||
                    switch (tb.Type)
 | 
			
		||||
                    {
 | 
			
		||||
                        case SelectTableInfoType.Parent:
 | 
			
		||||
                        case SelectTableInfoType.RawJoin:
 | 
			
		||||
                            continue;
 | 
			
		||||
                        case SelectTableInfoType.LeftJoin:
 | 
			
		||||
                            sb.Append(" \r\nLEFT JOIN ");
 | 
			
		||||
                            break;
 | 
			
		||||
                        case SelectTableInfoType.InnerJoin:
 | 
			
		||||
                            sb.Append(" \r\nINNER JOIN ");
 | 
			
		||||
                            break;
 | 
			
		||||
                        case SelectTableInfoType.RightJoin:
 | 
			
		||||
                            sb.Append(" \r\nRIGHT JOIN ");
 | 
			
		||||
                            break;
 | 
			
		||||
                    }
 | 
			
		||||
                    sb.Append(_commonUtils.QuoteSqlName(tbUnion[tb.Table.Type])).Append(" ").Append(_aliasRule?.Invoke(tb.Table.Type, tb.Alias) ?? tb.Alias).Append(" ON ").Append(tb.On ?? tb.NavigateCondition);
 | 
			
		||||
                    if (!string.IsNullOrEmpty(tb.Cascade)) sb.Append(" AND ").Append(tb.Cascade);
 | 
			
		||||
                    if (!string.IsNullOrEmpty(tb.On) && !string.IsNullOrEmpty(tb.NavigateCondition)) sbnav.Append(" AND (").Append(tb.NavigateCondition).Append(")");
 | 
			
		||||
                }
 | 
			
		||||
                if (_join.Length > 0) sb.Append(_join);
 | 
			
		||||
 | 
			
		||||
                sbnav.Append(_where);
 | 
			
		||||
                if (!string.IsNullOrEmpty(_tables[0].Cascade))
 | 
			
		||||
                    sbnav.Append(" AND ").Append(_tables[0].Cascade);
 | 
			
		||||
 | 
			
		||||
                if (sbnav.Length > 0)
 | 
			
		||||
                {
 | 
			
		||||
                    sb.Append(" \r\nWHERE ").Append(sbnav.Remove(0, 5));
 | 
			
		||||
                }
 | 
			
		||||
                if (string.IsNullOrEmpty(_groupby) == false)
 | 
			
		||||
                {
 | 
			
		||||
                    sb.Append(_groupby);
 | 
			
		||||
                    if (string.IsNullOrEmpty(_having) == false)
 | 
			
		||||
                        sb.Append(" \r\nHAVING ").Append(_having.Substring(5));
 | 
			
		||||
                }
 | 
			
		||||
                if (_skip <= 0)
 | 
			
		||||
                    sb.Append(_orderby);
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    sb.Insert(0, "WITH t AS ( ").Append(" ) SELECT t.* FROM t where __rownum__");
 | 
			
		||||
                    if (_limit > 0)
 | 
			
		||||
                        sb.Append(" between ").Append(_skip + 1).Append(" and ").Append(_skip + _limit);
 | 
			
		||||
                    else
 | 
			
		||||
                        sb.Append(" > ").Append(_skip);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                sbnav.Clear();
 | 
			
		||||
                if (tbUnionsGt0) sb.Append(") ftb");
 | 
			
		||||
            }
 | 
			
		||||
            return sb.Append(_tosqlAppendContent).ToString();
 | 
			
		||||
        }
 | 
			
		||||
        #endregion
 | 
			
		||||
 | 
			
		||||
        #region SqlServer 2012+ offset feach next
 | 
			
		||||
        internal static string ToSqlStaticOffsetFetchNext(CommonUtils _commonUtils, CommonExpression _commonExpression, string _select, bool _distinct, string field, StringBuilder _join, StringBuilder _where, string _groupby, string _having, string _orderby, int _skip, int _limit, List<SelectTableInfo> _tables, List<Dictionary<Type, string>> tbUnions, Func<Type, string, string> _aliasRule, string _tosqlAppendContent, List<GlobalFilter.Item> _whereGlobalFilter, IFreeSql _orm)
 | 
			
		||||
        {
 | 
			
		||||
            if (_orm.CodeFirst.IsAutoSyncStructure)
 | 
			
		||||
                _orm.CodeFirst.SyncStructure(_tables.Select(a => a.Table.Type).ToArray());
 | 
			
		||||
 | 
			
		||||
            if (_whereGlobalFilter.Any())
 | 
			
		||||
                foreach (var tb in _tables.Where(a => a.Type != SelectTableInfoType.Parent))
 | 
			
		||||
                    tb.Cascade = _commonExpression.GetWhereCascadeSql(tb, _whereGlobalFilter, true);
 | 
			
		||||
 | 
			
		||||
            var sb = new StringBuilder();
 | 
			
		||||
            var tbUnionsGt0 = tbUnions.Count > 1;
 | 
			
		||||
            for (var tbUnionsIdx = 0; tbUnionsIdx < tbUnions.Count; tbUnionsIdx++)
 | 
			
		||||
            {
 | 
			
		||||
                if (tbUnionsIdx > 0) sb.Append("\r\n \r\nUNION ALL\r\n \r\n");
 | 
			
		||||
                if (tbUnionsGt0) sb.Append(_select).Append(" * from (");
 | 
			
		||||
                var tbUnion = tbUnions[tbUnionsIdx];
 | 
			
		||||
                
 | 
			
		||||
                var sbnav = new StringBuilder();
 | 
			
		||||
                sb.Append(_select);
 | 
			
		||||
                if (_distinct) sb.Append("DISTINCT ");
 | 
			
		||||
                if (_skip <= 0 && _limit > 0) sb.Append("TOP ").Append(_limit).Append(" ");
 | 
			
		||||
                sb.Append(field);
 | 
			
		||||
                sb.Append(" \r\nFROM ");
 | 
			
		||||
                var tbsjoin = _tables.Where(a => a.Type != SelectTableInfoType.From).ToArray();
 | 
			
		||||
                var tbsfrom = _tables.Where(a => a.Type == SelectTableInfoType.From).ToArray();
 | 
			
		||||
                for (var a = 0; a < tbsfrom.Length; a++)
 | 
			
		||||
                {
 | 
			
		||||
                    sb.Append(_commonUtils.QuoteSqlName(tbUnion[tbsfrom[a].Table.Type])).Append(" ").Append(_aliasRule?.Invoke(tbsfrom[a].Table.Type, tbsfrom[a].Alias) ?? tbsfrom[a].Alias);
 | 
			
		||||
                    if (tbsjoin.Length > 0)
 | 
			
		||||
                    {
 | 
			
		||||
                        //如果存在 join 查询,则处理 from t1, t2 改为 from t1 inner join t2 on 1 = 1
 | 
			
		||||
                        for (var b = 1; b < tbsfrom.Length; b++)
 | 
			
		||||
                        {
 | 
			
		||||
                            sb.Append(" \r\nLEFT JOIN ").Append(_commonUtils.QuoteSqlName(tbUnion[tbsfrom[b].Table.Type])).Append(" ").Append(_aliasRule?.Invoke(tbsfrom[b].Table.Type, tbsfrom[b].Alias) ?? tbsfrom[b].Alias);
 | 
			
		||||
 | 
			
		||||
                            if (string.IsNullOrEmpty(tbsfrom[b].NavigateCondition) && string.IsNullOrEmpty(tbsfrom[b].On) && string.IsNullOrEmpty(tbsfrom[b].Cascade)) sb.Append(" ON 1 = 1");
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                var onSql = tbsfrom[b].NavigateCondition ?? tbsfrom[b].On;
 | 
			
		||||
                                sb.Append(" ON ").Append(onSql);
 | 
			
		||||
                                if (string.IsNullOrEmpty(tbsfrom[b].Cascade) == false)
 | 
			
		||||
                                {
 | 
			
		||||
                                    if (string.IsNullOrEmpty(onSql)) sb.Append(tbsfrom[b].Cascade);
 | 
			
		||||
                                    else sb.Append(" AND ").Append(tbsfrom[b].Cascade);
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        if (!string.IsNullOrEmpty(tbsfrom[a].NavigateCondition)) sbnav.Append(" AND (").Append(tbsfrom[a].NavigateCondition).Append(")");
 | 
			
		||||
                        if (!string.IsNullOrEmpty(tbsfrom[a].On)) sbnav.Append(" AND (").Append(tbsfrom[a].On).Append(")");
 | 
			
		||||
                        if (a > 0 && !string.IsNullOrEmpty(tbsfrom[a].Cascade)) sbnav.Append(" AND ").Append(tbsfrom[a].Cascade);
 | 
			
		||||
                    }
 | 
			
		||||
                    if (a < tbsfrom.Length - 1) sb.Append(", ");
 | 
			
		||||
                }
 | 
			
		||||
                foreach (var tb in tbsjoin)
 | 
			
		||||
                {
 | 
			
		||||
                    switch (tb.Type)
 | 
			
		||||
                    {
 | 
			
		||||
                        case SelectTableInfoType.Parent:
 | 
			
		||||
                        case SelectTableInfoType.RawJoin:
 | 
			
		||||
                            continue;
 | 
			
		||||
                        case SelectTableInfoType.LeftJoin:
 | 
			
		||||
                            sb.Append(" \r\nLEFT JOIN ");
 | 
			
		||||
                            break;
 | 
			
		||||
                        case SelectTableInfoType.InnerJoin:
 | 
			
		||||
                            sb.Append(" \r\nINNER JOIN ");
 | 
			
		||||
                            break;
 | 
			
		||||
                        case SelectTableInfoType.RightJoin:
 | 
			
		||||
                            sb.Append(" \r\nRIGHT JOIN ");
 | 
			
		||||
                            break;
 | 
			
		||||
                    }
 | 
			
		||||
                    sb.Append(_commonUtils.QuoteSqlName(tbUnion[tb.Table.Type])).Append(" ").Append(_aliasRule?.Invoke(tb.Table.Type, tb.Alias) ?? tb.Alias).Append(" ON ").Append(tb.On ?? tb.NavigateCondition);
 | 
			
		||||
                    if (!string.IsNullOrEmpty(tb.Cascade)) sb.Append(" AND ").Append(tb.Cascade);
 | 
			
		||||
                    if (!string.IsNullOrEmpty(tb.On) && !string.IsNullOrEmpty(tb.NavigateCondition)) sbnav.Append(" AND (").Append(tb.NavigateCondition).Append(")");
 | 
			
		||||
                }
 | 
			
		||||
                if (_join.Length > 0) sb.Append(_join);
 | 
			
		||||
 | 
			
		||||
                sbnav.Append(_where);
 | 
			
		||||
                if (!string.IsNullOrEmpty(_tables[0].Cascade))
 | 
			
		||||
                    sbnav.Append(" AND ").Append(_tables[0].Cascade);
 | 
			
		||||
 | 
			
		||||
                if (sbnav.Length > 0)
 | 
			
		||||
                {
 | 
			
		||||
                    sb.Append(" \r\nWHERE ").Append(sbnav.Remove(0, 5));
 | 
			
		||||
                }
 | 
			
		||||
                if (string.IsNullOrEmpty(_groupby) == false)
 | 
			
		||||
                {
 | 
			
		||||
                    sb.Append(_groupby);
 | 
			
		||||
                    if (string.IsNullOrEmpty(_having) == false)
 | 
			
		||||
                        sb.Append(" \r\nHAVING ").Append(_having.Substring(5));
 | 
			
		||||
                }
 | 
			
		||||
                if (_skip > 0)
 | 
			
		||||
                {
 | 
			
		||||
                    if (string.IsNullOrEmpty(_orderby))
 | 
			
		||||
                    {
 | 
			
		||||
                        if (string.IsNullOrEmpty(_groupby))
 | 
			
		||||
                        {
 | 
			
		||||
                            var pktb = _tables.Where(a => a.Table.Primarys.Any()).FirstOrDefault();
 | 
			
		||||
                            if (pktb != null) _orderby = string.Concat(" \r\nORDER BY ", pktb.Alias, ".", _commonUtils.QuoteSqlName(pktb?.Table.Primarys.First().Attribute.Name));
 | 
			
		||||
                            else _orderby = string.Concat(" \r\nORDER BY ", _tables.First().Alias, ".", _commonUtils.QuoteSqlName(_tables.First().Table.Columns.First().Value.Attribute.Name));
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                            _orderby = _groupby.Replace("GROUP BY ", "ORDER BY ");
 | 
			
		||||
                    }
 | 
			
		||||
                    sb.Append(_orderby).Append($" \r\nOFFSET {_skip} ROW");
 | 
			
		||||
                    if (_limit > 0) sb.Append($" \r\nFETCH NEXT {_limit} ROW ONLY");
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    sb.Append(_orderby);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                sbnav.Clear();
 | 
			
		||||
                if (tbUnionsGt0) sb.Append(") ftb");
 | 
			
		||||
            }
 | 
			
		||||
            return sb.Append(_tosqlAppendContent).ToString();
 | 
			
		||||
        }
 | 
			
		||||
        #endregion
 | 
			
		||||
 | 
			
		||||
        public CustomSqlServerSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override ISelect<T1, T2> From<T2>(Expression<Func<ISelectFromExpression<T1>, T2, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new OdbcSqlServerSelect<T1, T2>(_orm, _commonUtils, _commonExpression, null); CustomSqlServerSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
 | 
			
		||||
        public override ISelect<T1, T2, T3> From<T2, T3>(Expression<Func<ISelectFromExpression<T1>, T2, T3, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new OdbcSqlServerSelect<T1, T2, T3>(_orm, _commonUtils, _commonExpression, null); CustomSqlServerSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
 | 
			
		||||
        public override ISelect<T1, T2, T3, T4> From<T2, T3, T4>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new OdbcSqlServerSelect<T1, T2, T3, T4>(_orm, _commonUtils, _commonExpression, null); CustomSqlServerSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
 | 
			
		||||
        public override ISelect<T1, T2, T3, T4, T5> From<T2, T3, T4, T5>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new OdbcSqlServerSelect<T1, T2, T3, T4, T5>(_orm, _commonUtils, _commonExpression, null); CustomSqlServerSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
 | 
			
		||||
        public override ISelect<T1, T2, T3, T4, T5, T6> From<T2, T3, T4, T5, T6>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new OdbcSqlServerSelect<T1, T2, T3, T4, T5, T6>(_orm, _commonUtils, _commonExpression, null); CustomSqlServerSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
 | 
			
		||||
        public override ISelect<T1, T2, T3, T4, T5, T6, T7> From<T2, T3, T4, T5, T6, T7>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new OdbcSqlServerSelect<T1, T2, T3, T4, T5, T6, T7>(_orm, _commonUtils, _commonExpression, null); CustomSqlServerSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
 | 
			
		||||
        public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8> From<T2, T3, T4, T5, T6, T7, T8>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new OdbcSqlServerSelect<T1, T2, T3, T4, T5, T6, T7, T8>(_orm, _commonUtils, _commonExpression, null); CustomSqlServerSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
 | 
			
		||||
        public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9> From<T2, T3, T4, T5, T6, T7, T8, T9>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new OdbcSqlServerSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9>(_orm, _commonUtils, _commonExpression, null); CustomSqlServerSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
 | 
			
		||||
        public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> From<T2, T3, T4, T5, T6, T7, T8, T9, T10>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, T10, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new OdbcSqlServerSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(_orm, _commonUtils, _commonExpression, null); CustomSqlServerSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
 | 
			
		||||
        
 | 
			
		||||
        public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> From<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new OdbcSqlServerSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>(_orm, _commonUtils, _commonExpression, null); CustomSqlServerSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
 | 
			
		||||
        public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> From<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new OdbcSqlServerSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>(_orm, _commonUtils, _commonExpression, null); CustomSqlServerSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
 | 
			
		||||
        public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> From<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new OdbcSqlServerSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>(_orm, _commonUtils, _commonExpression, null); CustomSqlServerSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
 | 
			
		||||
        public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14> From<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new OdbcSqlServerSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>(_orm, _commonUtils, _commonExpression, null); CustomSqlServerSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
 | 
			
		||||
        public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> From<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new OdbcSqlServerSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>(_orm, _commonUtils, _commonExpression, null); CustomSqlServerSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
 | 
			
		||||
        public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16> From<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new OdbcSqlServerSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>(_orm, _commonUtils, _commonExpression, null); CustomSqlServerSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
 | 
			
		||||
        public override string ToSql(string field = null) => ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcSqlServerSelect<T1, T2> : FreeSql.Internal.CommonProvider.Select2Provider<T1, T2> where T2 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcSqlServerSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomSqlServerSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcSqlServerSelect<T1, T2, T3> : FreeSql.Internal.CommonProvider.Select3Provider<T1, T2, T3> where T2 : class where T3 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcSqlServerSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomSqlServerSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcSqlServerSelect<T1, T2, T3, T4> : FreeSql.Internal.CommonProvider.Select4Provider<T1, T2, T3, T4> where T2 : class where T3 : class where T4 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcSqlServerSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomSqlServerSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcSqlServerSelect<T1, T2, T3, T4, T5> : FreeSql.Internal.CommonProvider.Select5Provider<T1, T2, T3, T4, T5> where T2 : class where T3 : class where T4 : class where T5 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcSqlServerSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomSqlServerSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcSqlServerSelect<T1, T2, T3, T4, T5, T6> : FreeSql.Internal.CommonProvider.Select6Provider<T1, T2, T3, T4, T5, T6> where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcSqlServerSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomSqlServerSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcSqlServerSelect<T1, T2, T3, T4, T5, T6, T7> : FreeSql.Internal.CommonProvider.Select7Provider<T1, T2, T3, T4, T5, T6, T7> where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcSqlServerSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomSqlServerSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcSqlServerSelect<T1, T2, T3, T4, T5, T6, T7, T8> : FreeSql.Internal.CommonProvider.Select8Provider<T1, T2, T3, T4, T5, T6, T7, T8> where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcSqlServerSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomSqlServerSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcSqlServerSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9> : FreeSql.Internal.CommonProvider.Select9Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9> where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcSqlServerSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomSqlServerSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcSqlServerSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> : FreeSql.Internal.CommonProvider.Select10Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcSqlServerSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomSqlServerSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    class OdbcSqlServerSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> : FreeSql.Internal.CommonProvider.Select11Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class where T11 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcSqlServerSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomSqlServerSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcSqlServerSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> : FreeSql.Internal.CommonProvider.Select12Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class where T11 : class where T12 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcSqlServerSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomSqlServerSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcSqlServerSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> : FreeSql.Internal.CommonProvider.Select13Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class where T11 : class where T12 : class where T13 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcSqlServerSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomSqlServerSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcSqlServerSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14> : FreeSql.Internal.CommonProvider.Select14Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14> where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class where T11 : class where T12 : class where T13 : class where T14 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcSqlServerSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomSqlServerSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcSqlServerSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> : FreeSql.Internal.CommonProvider.Select15Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class where T11 : class where T12 : class where T13 : class where T14 : class where T15 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcSqlServerSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomSqlServerSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcSqlServerSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16> : FreeSql.Internal.CommonProvider.Select16Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16> where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class where T11 : class where T12 : class where T13 : class where T14 : class where T15 : class where T16 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcSqlServerSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomSqlServerSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,173 @@
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using FreeSql.Internal.Model;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Data;
 | 
			
		||||
using System.Data.Common;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Custom.SqlServer
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    class CustomSqlServerUpdate<T1> : Internal.CommonProvider.UpdateProvider<T1>
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        public CustomSqlServerUpdate(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere)
 | 
			
		||||
            : base(orm, commonUtils, commonExpression, dywhere)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override int ExecuteAffrows() => base.SplitExecuteAffrows(_batchRowsLimit > 0 ? _batchRowsLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 2100);
 | 
			
		||||
        public override List<T1> ExecuteUpdated() => base.SplitExecuteUpdated(_batchRowsLimit > 0 ? _batchRowsLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 2100);
 | 
			
		||||
 | 
			
		||||
        protected override List<T1> RawExecuteUpdated()
 | 
			
		||||
        {
 | 
			
		||||
            var ret = new List<T1>();
 | 
			
		||||
            DbParameter[] dbParms = null;
 | 
			
		||||
            StringBuilder sbret = null;
 | 
			
		||||
            ToSqlFetch(sb =>
 | 
			
		||||
            {
 | 
			
		||||
                if (dbParms == null)
 | 
			
		||||
                {
 | 
			
		||||
                    dbParms = _params.Concat(_paramsSource).ToArray();
 | 
			
		||||
                    sbret = new StringBuilder();
 | 
			
		||||
                    sbret.Append(" OUTPUT ");
 | 
			
		||||
 | 
			
		||||
                    var colidx = 0;
 | 
			
		||||
                    foreach (var col in _table.Columns.Values)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (colidx > 0) sbret.Append(", ");
 | 
			
		||||
                        sbret.Append(_commonUtils.RereadColumn(col, $"INSERTED.{_commonUtils.QuoteSqlName(col.Attribute.Name)}")).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
 | 
			
		||||
                        ++colidx;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                var sql = sb.ToString();
 | 
			
		||||
                var validx = sql.IndexOf(" \r\nWHERE ");
 | 
			
		||||
                if (validx == -1) throw new ArgumentException(CoreStrings.S_NotFound_Name("WHERE"));
 | 
			
		||||
                sql = sb.Clear().Append(sql.Substring(0, validx))
 | 
			
		||||
                    .Append(sbret)
 | 
			
		||||
                    .Append(sql.Substring(validx)).ToString();
 | 
			
		||||
 | 
			
		||||
                var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Update, sql, dbParms);
 | 
			
		||||
                _orm.Aop.CurdBeforeHandler?.Invoke(this, before);
 | 
			
		||||
 | 
			
		||||
                Exception exception = null;
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    var rettmp = _orm.Ado.Query<T1>(_table.TypeLazy ?? _table.Type, _connection, _transaction, CommandType.Text, sql, _commandTimeout, dbParms);
 | 
			
		||||
                    ValidateVersionAndThrow(rettmp.Count, sql, dbParms);
 | 
			
		||||
                    ret.AddRange(rettmp);
 | 
			
		||||
                }
 | 
			
		||||
                catch (Exception ex)
 | 
			
		||||
                {
 | 
			
		||||
                    exception = ex;
 | 
			
		||||
                    throw ex;
 | 
			
		||||
                }
 | 
			
		||||
                finally
 | 
			
		||||
                {
 | 
			
		||||
                    var after = new Aop.CurdAfterEventArgs(before, exception, ret);
 | 
			
		||||
                    _orm.Aop.CurdAfterHandler?.Invoke(this, after);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            sbret?.Clear();
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override void ToSqlCase(StringBuilder caseWhen, ColumnInfo[] primarys)
 | 
			
		||||
        {
 | 
			
		||||
            if (primarys.Length == 1)
 | 
			
		||||
            {
 | 
			
		||||
                var pk = primarys.First();
 | 
			
		||||
                caseWhen.Append(_commonUtils.RereadColumn(pk, _commonUtils.QuoteSqlName(pk.Attribute.Name)));
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            caseWhen.Append("(");
 | 
			
		||||
            var pkidx = 0;
 | 
			
		||||
            foreach (var pk in primarys)
 | 
			
		||||
            {
 | 
			
		||||
                if (pkidx > 0) caseWhen.Append(" + '+' + ");
 | 
			
		||||
                caseWhen.Append("cast(").Append(_commonUtils.RereadColumn(pk, _commonUtils.QuoteSqlName(pk.Attribute.Name))).Append(" as varchar)");
 | 
			
		||||
                ++pkidx;
 | 
			
		||||
            }
 | 
			
		||||
            caseWhen.Append(")");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override void ToSqlWhen(StringBuilder sb, ColumnInfo[] primarys, object d)
 | 
			
		||||
        {
 | 
			
		||||
            if (primarys.Length == 1)
 | 
			
		||||
            {
 | 
			
		||||
                sb.Append(_commonUtils.FormatSql("{0}", primarys[0].GetDbValue(d)));
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            var pkidx = 0;
 | 
			
		||||
            foreach (var pk in primarys)
 | 
			
		||||
            {
 | 
			
		||||
                if (pkidx > 0) sb.Append(" + '+' + ");
 | 
			
		||||
                sb.Append("cast(").Append(_commonUtils.FormatSql("{0}", pk.GetDbValue(d))).Append(" as varchar)");
 | 
			
		||||
                ++pkidx;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
#if net40
 | 
			
		||||
#else
 | 
			
		||||
        public override Task<int> ExecuteAffrowsAsync(CancellationToken cancellationToken = default) => base.SplitExecuteAffrowsAsync(_batchRowsLimit > 0 ? _batchRowsLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 2100, cancellationToken);
 | 
			
		||||
        public override Task<List<T1>> ExecuteUpdatedAsync(CancellationToken cancellationToken = default) => base.SplitExecuteUpdatedAsync(_batchRowsLimit > 0 ? _batchRowsLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 2100, cancellationToken);
 | 
			
		||||
 | 
			
		||||
        async protected override Task<List<T1>> RawExecuteUpdatedAsync(CancellationToken cancellationToken = default)
 | 
			
		||||
        {
 | 
			
		||||
            var ret = new List<T1>();
 | 
			
		||||
            DbParameter[] dbParms = null;
 | 
			
		||||
            StringBuilder sbret = null;
 | 
			
		||||
            await ToSqlFetchAsync(async sb =>
 | 
			
		||||
            {
 | 
			
		||||
                if (dbParms == null)
 | 
			
		||||
                {
 | 
			
		||||
                    dbParms = _params.Concat(_paramsSource).ToArray();
 | 
			
		||||
                    sbret = new StringBuilder();
 | 
			
		||||
                    sbret.Append(" OUTPUT ");
 | 
			
		||||
 | 
			
		||||
                    var colidx = 0;
 | 
			
		||||
                    foreach (var col in _table.Columns.Values)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (colidx > 0) sbret.Append(", ");
 | 
			
		||||
                        sbret.Append(_commonUtils.RereadColumn(col, $"INSERTED.{_commonUtils.QuoteSqlName(col.Attribute.Name)}")).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
 | 
			
		||||
                        ++colidx;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                var sql = sb.ToString();
 | 
			
		||||
                var validx = sql.IndexOf(" \r\nWHERE ");
 | 
			
		||||
                if (validx == -1) throw new ArgumentException(CoreStrings.S_NotFound_Name("WHERE"));
 | 
			
		||||
                sql = sb.Clear().Append(sql.Substring(0, validx))
 | 
			
		||||
                    .Append(sbret)
 | 
			
		||||
                    .Append(sql.Substring(validx)).ToString();
 | 
			
		||||
 | 
			
		||||
                var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Update, sql, dbParms);
 | 
			
		||||
                _orm.Aop.CurdBeforeHandler?.Invoke(this, before);
 | 
			
		||||
 | 
			
		||||
                Exception exception = null;
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    var rettmp = await _orm.Ado.QueryAsync<T1>(_table.TypeLazy ?? _table.Type, _connection, _transaction, CommandType.Text, sql, _commandTimeout, dbParms, cancellationToken);
 | 
			
		||||
                    ValidateVersionAndThrow(rettmp.Count, sql, dbParms);
 | 
			
		||||
                    ret.AddRange(rettmp);
 | 
			
		||||
                }
 | 
			
		||||
                catch (Exception ex)
 | 
			
		||||
                {
 | 
			
		||||
                    exception = ex;
 | 
			
		||||
                    throw ex;
 | 
			
		||||
                }
 | 
			
		||||
                finally
 | 
			
		||||
                {
 | 
			
		||||
                    var after = new Aop.CurdAfterEventArgs(before, exception, ret);
 | 
			
		||||
                    _orm.Aop.CurdAfterHandler?.Invoke(this, after);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            sbret?.Clear();
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,83 @@
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using FreeSql.Internal.Model;
 | 
			
		||||
using FreeSql.Internal.ObjectPool;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections;
 | 
			
		||||
using System.Data.Common;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Custom.SqlServer
 | 
			
		||||
{
 | 
			
		||||
    class CustomSqlServerAdo : FreeSql.Internal.CommonProvider.AdoProvider
 | 
			
		||||
    {
 | 
			
		||||
        DbProviderFactory Factory => FreeSqlCustomAdapterGlobalExtensions.GetDbProviderFactory(_util._orm);
 | 
			
		||||
 | 
			
		||||
        public CustomSqlServerAdo() : base(DataType.OdbcSqlServer, null, null) { }
 | 
			
		||||
        public CustomSqlServerAdo(CommonUtils util, string masterConnectionString, string[] slaveConnectionStrings, Func<DbConnection> connectionFactory) : base(DataType.OdbcSqlServer, masterConnectionString, slaveConnectionStrings)
 | 
			
		||||
        {
 | 
			
		||||
            base._util = util;
 | 
			
		||||
            if (connectionFactory != null)
 | 
			
		||||
            {
 | 
			
		||||
                var pool = new FreeSql.Internal.CommonProvider.DbConnectionPool(DataType.SqlServer, connectionFactory);
 | 
			
		||||
                MasterPool = pool;
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            throw new Exception(CoreStrings.S_CustomAdapter_OnlySuppport_UseConnectionFactory);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        string[] ncharDbTypes = new[] { "NVARCHAR", "NCHAR", "NTEXT" };
 | 
			
		||||
        public override object AddslashesProcessParam(object param, Type mapType, ColumnInfo mapColumn)
 | 
			
		||||
        {
 | 
			
		||||
            if (param == null) return "NULL";
 | 
			
		||||
            if (mapType != null && mapType != param.GetType() && (param is IEnumerable == false))
 | 
			
		||||
                param = Utils.GetDataReaderValue(mapType, param);
 | 
			
		||||
 | 
			
		||||
            if (param is bool || param is bool?)
 | 
			
		||||
                return (bool)param ? 1 : 0;
 | 
			
		||||
            else if (param is string)
 | 
			
		||||
            {
 | 
			
		||||
                if (mapColumn != null && mapColumn.CsType.NullableTypeOrThis() == typeof(string) && ncharDbTypes.Any(a => mapColumn.Attribute.DbType.Contains(a)) == false)
 | 
			
		||||
                    return string.Concat("'", param.ToString().Replace("'", "''"), "'");
 | 
			
		||||
                return string.Concat("N'", param.ToString().Replace("'", "''"), "'");
 | 
			
		||||
            }
 | 
			
		||||
            else if (param is char)
 | 
			
		||||
                return string.Concat("'", param.ToString().Replace("'", "''").Replace('\0', ' '), "'");
 | 
			
		||||
            else if (param is Enum)
 | 
			
		||||
                return ((Enum)param).ToInt64();
 | 
			
		||||
            else if (decimal.TryParse(string.Concat(param), out var trydec))
 | 
			
		||||
                return param;
 | 
			
		||||
            else if (param is DateTime || param is DateTime?)
 | 
			
		||||
            {
 | 
			
		||||
                if (param.Equals(DateTime.MinValue) == true) param = new DateTime(1970, 1, 1);
 | 
			
		||||
                return string.Concat("'", ((DateTime)param).ToString("yyyy-MM-dd HH:mm:ss.fff"), "'");
 | 
			
		||||
            }
 | 
			
		||||
            else if (param is DateTimeOffset || param is DateTimeOffset?)
 | 
			
		||||
            {
 | 
			
		||||
                if (param.Equals(DateTimeOffset.MinValue) == true) param = new DateTimeOffset(new DateTime(1970, 1, 1), TimeSpan.Zero);
 | 
			
		||||
                return string.Concat("'", ((DateTimeOffset)param).ToString("yyyy-MM-dd HH:mm:ss.fff zzzz"), "'");
 | 
			
		||||
            }
 | 
			
		||||
            else if (param is TimeSpan || param is TimeSpan?)
 | 
			
		||||
                return ((TimeSpan)param).TotalSeconds;
 | 
			
		||||
            else if (param is byte[])
 | 
			
		||||
                return $"0x{CommonUtils.BytesSqlRaw(param as byte[])}";
 | 
			
		||||
            else if (param is IEnumerable) 
 | 
			
		||||
                return AddslashesIEnumerable(param, mapType, mapColumn);
 | 
			
		||||
 | 
			
		||||
            return string.Concat("'", param.ToString().Replace("'", "''"), "'");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override DbCommand CreateCommand()
 | 
			
		||||
        {
 | 
			
		||||
            return Factory.CreateCommand();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override void ReturnConnection(IObjectPool<DbConnection> pool, Object<DbConnection> conn, Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            pool.Return(conn);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override DbParameter[] GetDbParamtersByObject(string sql, object obj) => _util.GetDbParamtersByObject(sql, obj);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,499 @@
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using FreeSql.Internal.Model;
 | 
			
		||||
using FreeSql.Internal.ObjectPool;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Data;
 | 
			
		||||
using System.Data.Common;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Custom.SqlServer
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    class CustomSqlServerCodeFirst : Internal.CommonProvider.CodeFirstProvider
 | 
			
		||||
    {
 | 
			
		||||
        public override bool IsNoneCommandParameter { get => true; set => base.IsNoneCommandParameter = true; }
 | 
			
		||||
        public CustomSqlServerCodeFirst(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) : base(orm, commonUtils, commonExpression) { }
 | 
			
		||||
 | 
			
		||||
        static object _dicCsToDbLock = new object();
 | 
			
		||||
        static Dictionary<string, CsToDb<DbType>> _dicCsToDb = new Dictionary<string, CsToDb<DbType>>() {
 | 
			
		||||
                { typeof(bool).FullName, CsToDb.New(DbType.Boolean, "bit","bit NOT NULL", null, false, false) },{ typeof(bool?).FullName, CsToDb.New(DbType.Boolean, "bit","bit", null, true, null) },
 | 
			
		||||
 | 
			
		||||
                { typeof(sbyte).FullName, CsToDb.New(DbType.SByte, "smallint", "smallint NOT NULL", false, false, 0) },{ typeof(sbyte?).FullName, CsToDb.New(DbType.SByte, "smallint", "smallint", false, true, null) },
 | 
			
		||||
                { typeof(short).FullName, CsToDb.New(DbType.Int16, "smallint","smallint NOT NULL", false, false, 0) },{ typeof(short?).FullName, CsToDb.New(DbType.Int16, "smallint", "smallint", false, true, null) },
 | 
			
		||||
                { typeof(int).FullName, CsToDb.New(DbType.Int32, "int", "int NOT NULL", false, false, 0) },{ typeof(int?).FullName, CsToDb.New(DbType.Int32, "int", "int", false, true, null) },
 | 
			
		||||
                { typeof(long).FullName, CsToDb.New(DbType.Int64, "bigint","bigint NOT NULL", false, false, 0) },{ typeof(long?).FullName, CsToDb.New(DbType.Int64, "bigint","bigint", false, true, null) },
 | 
			
		||||
 | 
			
		||||
                { typeof(byte).FullName, CsToDb.New(DbType.Byte, "tinyint","tinyint NOT NULL", true, false, 0) },{ typeof(byte?).FullName, CsToDb.New(DbType.Byte, "tinyint","tinyint", true, true, null) },
 | 
			
		||||
                { typeof(ushort).FullName, CsToDb.New(DbType.UInt16, "int","int NOT NULL", true, false, 0) },{ typeof(ushort?).FullName, CsToDb.New(DbType.UInt16, "int", "int", true, true, null) },
 | 
			
		||||
                { typeof(uint).FullName, CsToDb.New(DbType.UInt32, "bigint", "bigint NOT NULL", true, false, 0) },{ typeof(uint?).FullName, CsToDb.New(DbType.UInt32, "bigint", "bigint", true, true, null) },
 | 
			
		||||
                { typeof(ulong).FullName, CsToDb.New(DbType.UInt64, "decimal", "decimal(20,0) NOT NULL", true, false, 0) },{ typeof(ulong?).FullName, CsToDb.New(DbType.UInt64, "decimal", "decimal(20,0)", true, true, null) },
 | 
			
		||||
 | 
			
		||||
                { typeof(double).FullName, CsToDb.New(DbType.Double, "float", "float NOT NULL", false, false, 0) },{ typeof(double?).FullName, CsToDb.New(DbType.Double, "float", "float", false, true, null) },
 | 
			
		||||
                { typeof(float).FullName, CsToDb.New(DbType.Single, "real","real NOT NULL", false, false, 0) },{ typeof(float?).FullName, CsToDb.New(DbType.Single, "real","real", false, true, null) },
 | 
			
		||||
                { typeof(decimal).FullName, CsToDb.New(DbType.Decimal, "decimal", "decimal(10,2) NOT NULL", false, false, 0) },{ typeof(decimal?).FullName, CsToDb.New(DbType.Decimal, "decimal", "decimal(10,2)", false, true, null) },
 | 
			
		||||
 | 
			
		||||
                { typeof(TimeSpan).FullName, CsToDb.New(DbType.Time, "time","time NOT NULL", false, false, 0) },{ typeof(TimeSpan?).FullName, CsToDb.New(DbType.Time, "time", "time",false, true, null) },
 | 
			
		||||
                { typeof(DateTime).FullName, CsToDb.New(DbType.DateTime, "datetime", "datetime NOT NULL", false, false, new DateTime(1970,1,1)) },{ typeof(DateTime?).FullName, CsToDb.New(DbType.DateTime, "datetime", "datetime", false, true, null) },
 | 
			
		||||
                { typeof(DateTimeOffset).FullName, CsToDb.New(DbType.DateTimeOffset, "datetimeoffset", "datetimeoffset NOT NULL", false, false, new DateTimeOffset(new DateTime(1970,1,1), TimeSpan.Zero)) },{ typeof(DateTimeOffset?).FullName, CsToDb.New(DbType.DateTimeOffset, "datetimeoffset", "datetimeoffset", false, true, null) },
 | 
			
		||||
 | 
			
		||||
                { typeof(byte[]).FullName, CsToDb.New(DbType.Binary, "varbinary", "varbinary(255)", false, null, new byte[0]) },
 | 
			
		||||
                { typeof(string).FullName, CsToDb.New(DbType.String, "nvarchar", "nvarchar(255)", false, null, "") },
 | 
			
		||||
                { typeof(char).FullName, CsToDb.New(DbType.AnsiString, "char", "char(1) NULL", false, null, '\0') },
 | 
			
		||||
 | 
			
		||||
                { typeof(Guid).FullName, CsToDb.New(DbType.Guid, "uniqueidentifier", "uniqueidentifier NOT NULL", false, false, Guid.Empty) },{ typeof(Guid?).FullName, CsToDb.New(DbType.Guid, "uniqueidentifier", "uniqueidentifier", false, true, null) },
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
        public override DbInfoResult GetDbInfo(Type type)
 | 
			
		||||
        {
 | 
			
		||||
            if (_dicCsToDb.TryGetValue(type.FullName, out var trydc)) return new DbInfoResult((int)trydc.type, trydc.dbtype, trydc.dbtypeFull, trydc.isnullable, trydc.defaultValue);
 | 
			
		||||
            if (type.IsArray) return null;
 | 
			
		||||
            var enumType = type.IsEnum ? type : null;
 | 
			
		||||
            if (enumType == null && type.IsNullableType())
 | 
			
		||||
            {
 | 
			
		||||
                var genericTypes = type.GetGenericArguments();
 | 
			
		||||
                if (genericTypes.Length == 1 && genericTypes.First().IsEnum) enumType = genericTypes.First();
 | 
			
		||||
            }
 | 
			
		||||
            if (enumType != null)
 | 
			
		||||
            {
 | 
			
		||||
                var newItem = enumType.GetCustomAttributes(typeof(FlagsAttribute), false).Any() ?
 | 
			
		||||
                    CsToDb.New(DbType.Int32, "bigint", $"bigint{(type.IsEnum ? " NOT NULL" : "")}", false, type.IsEnum ? false : true, enumType.CreateInstanceGetDefaultValue()) :
 | 
			
		||||
                    CsToDb.New(DbType.Int64, "int", $"int{(type.IsEnum ? " NOT NULL" : "")}", false, type.IsEnum ? false : true, enumType.CreateInstanceGetDefaultValue());
 | 
			
		||||
                if (_dicCsToDb.ContainsKey(type.FullName) == false)
 | 
			
		||||
                {
 | 
			
		||||
                    lock (_dicCsToDbLock)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (_dicCsToDb.ContainsKey(type.FullName) == false)
 | 
			
		||||
                            _dicCsToDb.Add(type.FullName, newItem);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                return new DbInfoResult((int)newItem.type, newItem.dbtype, newItem.dbtypeFull, newItem.isnullable, newItem.defaultValue);
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void AddOrUpdateMS_Description(StringBuilder sb, string schema, string table, string comment)
 | 
			
		||||
        {
 | 
			
		||||
            if (string.IsNullOrEmpty(comment))
 | 
			
		||||
            {
 | 
			
		||||
                sb.AppendFormat(@"
 | 
			
		||||
IF ((SELECT COUNT(1) from fn_listextendedproperty('MS_Description', 
 | 
			
		||||
  'SCHEMA', N'{0}', 
 | 
			
		||||
  'TABLE', N'{1}', 
 | 
			
		||||
  NULL, NULL)) > 0) 
 | 
			
		||||
  EXEC sp_dropextendedproperty @name = N'MS_Description'
 | 
			
		||||
    , @level0type = 'SCHEMA', @level0name = N'{0}'
 | 
			
		||||
    , @level1type = 'TABLE', @level1name = N'{1}'
 | 
			
		||||
", schema.Replace("'", "''"), table.Replace("'", "''"));
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            sb.AppendFormat(@"
 | 
			
		||||
IF ((SELECT COUNT(1) from fn_listextendedproperty('MS_Description', 
 | 
			
		||||
  'SCHEMA', N'{0}', 
 | 
			
		||||
  'TABLE', N'{1}', 
 | 
			
		||||
  NULL, NULL)) > 0) 
 | 
			
		||||
  EXEC sp_updateextendedproperty @name = N'MS_Description', @value = N'{2}'
 | 
			
		||||
    , @level0type = 'SCHEMA', @level0name = N'{0}'
 | 
			
		||||
    , @level1type = 'TABLE', @level1name = N'{1}'
 | 
			
		||||
ELSE
 | 
			
		||||
  EXEC sp_addextendedproperty @name = N'MS_Description', @value = N'{2}'
 | 
			
		||||
    , @level0type = 'SCHEMA', @level0name = N'{0}'
 | 
			
		||||
    , @level1type = 'TABLE', @level1name = N'{1}'
 | 
			
		||||
", schema.Replace("'", "''"), table.Replace("'", "''"), comment?.Replace("'", "''") ?? "");
 | 
			
		||||
        }
 | 
			
		||||
        void AddOrUpdateMS_Description(StringBuilder sb, string schema, string table, string column, string comment)
 | 
			
		||||
        {
 | 
			
		||||
            if (string.IsNullOrEmpty(comment))
 | 
			
		||||
            {
 | 
			
		||||
                sb.AppendFormat(@"
 | 
			
		||||
IF ((SELECT COUNT(1) from fn_listextendedproperty('MS_Description', 
 | 
			
		||||
  'SCHEMA', N'{0}', 
 | 
			
		||||
  'TABLE', N'{1}', 
 | 
			
		||||
  'COLUMN', N'{2}')) > 0) 
 | 
			
		||||
  EXEC sp_dropextendedproperty @name = N'MS_Description'
 | 
			
		||||
    , @level0type = 'SCHEMA', @level0name = N'{0}'
 | 
			
		||||
    , @level1type = 'TABLE', @level1name = N'{1}'
 | 
			
		||||
    , @level2type = 'COLUMN', @level2name = N'{2}'
 | 
			
		||||
", schema.Replace("'", "''"), table.Replace("'", "''"), column.Replace("'", "''"));
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            sb.AppendFormat(@"
 | 
			
		||||
IF ((SELECT COUNT(1) from fn_listextendedproperty('MS_Description', 
 | 
			
		||||
  'SCHEMA', N'{0}', 
 | 
			
		||||
  'TABLE', N'{1}', 
 | 
			
		||||
  'COLUMN', N'{2}')) > 0) 
 | 
			
		||||
  EXEC sp_updateextendedproperty @name = N'MS_Description', @value = N'{3}'
 | 
			
		||||
    , @level0type = 'SCHEMA', @level0name = N'{0}'
 | 
			
		||||
    , @level1type = 'TABLE', @level1name = N'{1}'
 | 
			
		||||
    , @level2type = 'COLUMN', @level2name = N'{2}'
 | 
			
		||||
ELSE
 | 
			
		||||
  EXEC sp_addextendedproperty @name = N'MS_Description', @value = N'{3}'
 | 
			
		||||
    , @level0type = 'SCHEMA', @level0name = N'{0}'
 | 
			
		||||
    , @level1type = 'TABLE', @level1name = N'{1}'
 | 
			
		||||
    , @level2type = 'COLUMN', @level2name = N'{2}'
 | 
			
		||||
", schema.Replace("'", "''"), table.Replace("'", "''"), column.Replace("'", "''"), comment?.Replace("'", "''") ?? "");
 | 
			
		||||
        }
 | 
			
		||||
        protected override string GetComparisonDDLStatements(params TypeAndName[] objects)
 | 
			
		||||
        {
 | 
			
		||||
            Object<DbConnection> conn = null;
 | 
			
		||||
            string database = null;
 | 
			
		||||
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                conn = _orm.Ado.MasterPool.Get(TimeSpan.FromSeconds(5));
 | 
			
		||||
                database = conn.Value.Database;
 | 
			
		||||
 | 
			
		||||
                var sb = new StringBuilder();
 | 
			
		||||
                foreach (var obj in objects)
 | 
			
		||||
                {
 | 
			
		||||
                    if (sb.Length > 0) sb.Append("\r\n");
 | 
			
		||||
                    var tb = _commonUtils.GetTableByEntity(obj.entityType);
 | 
			
		||||
                    if (tb == null) throw new Exception(CoreStrings.S_Type_IsNot_Migrable(obj.entityType.FullName));
 | 
			
		||||
                    if (tb.Columns.Any() == false) throw new Exception(CoreStrings.S_Type_IsNot_Migrable_0Attributes(obj.entityType.FullName));
 | 
			
		||||
                    var tbname = _commonUtils.SplitTableName(tb.DbName);
 | 
			
		||||
                    if (tbname?.Length == 1) tbname = new[] { database, "dbo", tbname[0] };
 | 
			
		||||
                    if (tbname?.Length == 2) tbname = new[] { database, tbname[0], tbname[1] };
 | 
			
		||||
 | 
			
		||||
                    var tboldname = _commonUtils.SplitTableName(tb.DbOldName); //旧表名
 | 
			
		||||
                    if (tboldname?.Length == 1) tboldname = new[] { database, "dbo", tboldname[0] };
 | 
			
		||||
                    if (tboldname?.Length == 2) tboldname = new[] { database, tboldname[0], tboldname[1] };
 | 
			
		||||
                    if (string.IsNullOrEmpty(obj.tableName) == false)
 | 
			
		||||
                    {
 | 
			
		||||
                        var tbtmpname = _commonUtils.SplitTableName(obj.tableName);
 | 
			
		||||
                        if (tbtmpname?.Length == 1) tbtmpname = new[] { database, "dbo", tbtmpname[0] };
 | 
			
		||||
                        if (tbtmpname?.Length == 2) tbtmpname = new[] { database, tbtmpname[0], tbtmpname[1] };
 | 
			
		||||
                        if (tbname[0] != tbtmpname[0] || tbname[1] != tbtmpname[1] || tbname[2] != tbtmpname[2])
 | 
			
		||||
                        {
 | 
			
		||||
                            tbname = tbtmpname;
 | 
			
		||||
                            tboldname = null;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    //codefirst 不支持表名、模式名、数据库名中带 .
 | 
			
		||||
 | 
			
		||||
                    if (string.Compare(tbname[0], database, true) != 0 && LocalExecuteScalar(database, $" select 1 from sys.databases where name='{tbname[0]}'") == null) //创建数据库
 | 
			
		||||
                        LocalExecuteScalar(database, $"if not exists(select 1 from sys.databases where name='{tbname[0]}')\r\n\tcreate database [{tbname[0]}];");
 | 
			
		||||
                    if (string.Compare(tbname[1], "dbo", true) != 0 && LocalExecuteScalar(tbname[0], $" select 1 from sys.schemas where name='{tbname[1]}'") == null) //创建模式
 | 
			
		||||
                        LocalExecuteScalar(tbname[0], $"create schema [{tbname[1]}] authorization [dbo]");
 | 
			
		||||
 | 
			
		||||
                    var sbalter = new StringBuilder();
 | 
			
		||||
                    var istmpatler = false; //创建临时表,导入数据,删除旧表,修改
 | 
			
		||||
                    if (LocalExecuteScalar(tbname[0], $" select 1 from dbo.sysobjects where id = object_id(N'[{tbname[1]}].[{tbname[2]}]') and OBJECTPROPERTY(id, N'IsUserTable') = 1") == null)
 | 
			
		||||
                    { //表不存在
 | 
			
		||||
                        if (tboldname != null)
 | 
			
		||||
                        {
 | 
			
		||||
                            if (string.Compare(tboldname[0], tbname[0], true) != 0 && LocalExecuteScalar(database, $" select 1 from sys.databases where name='{tboldname[0]}'") == null ||
 | 
			
		||||
                                string.Compare(tboldname[1], tbname[1], true) != 0 && LocalExecuteScalar(tboldname[0], $" select 1 from sys.schemas where name='{tboldname[1]}'") == null ||
 | 
			
		||||
                                LocalExecuteScalar(tboldname[0], $" select 1 from dbo.sysobjects where id = object_id(N'[{tboldname[1]}].[{tboldname[2]}]') and OBJECTPROPERTY(id, N'IsUserTable') = 1") == null)
 | 
			
		||||
                                //数据库或模式或表不存在
 | 
			
		||||
                                tboldname = null;
 | 
			
		||||
                        }
 | 
			
		||||
                        if (tboldname == null)
 | 
			
		||||
                        {
 | 
			
		||||
                            //创建表
 | 
			
		||||
                            var createTableName = _commonUtils.QuoteSqlName(tbname[1], tbname[2]);
 | 
			
		||||
                            sb.Append("use [").Append(tbname[0]).Append("];\r\nCREATE TABLE ").Append(createTableName).Append(" ( ");
 | 
			
		||||
                            var pkidx = 0;
 | 
			
		||||
                            foreach (var tbcol in tb.ColumnsByPosition)
 | 
			
		||||
                            {
 | 
			
		||||
                                sb.Append(" \r\n  ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ").Append(tbcol.Attribute.DbType);
 | 
			
		||||
                                if (tbcol.Attribute.IsIdentity == true && tbcol.Attribute.DbType.IndexOf("identity", StringComparison.CurrentCultureIgnoreCase) == -1) sb.Append(" identity(1,1)");
 | 
			
		||||
                                if (tbcol.Attribute.IsPrimary == true)
 | 
			
		||||
                                {
 | 
			
		||||
                                    if (tb.Primarys.Length > 1)
 | 
			
		||||
                                    {
 | 
			
		||||
                                        if (pkidx == tb.Primarys.Length - 1)
 | 
			
		||||
                                            sb.Append(" primary key (").Append(string.Join(", ", tb.Primarys.Select(a => _commonUtils.QuoteSqlName(a.Attribute.Name)))).Append(")");
 | 
			
		||||
                                    }
 | 
			
		||||
                                    else
 | 
			
		||||
                                        sb.Append(" primary key");
 | 
			
		||||
                                    pkidx++;
 | 
			
		||||
                                }
 | 
			
		||||
                                sb.Append(",");
 | 
			
		||||
                            }
 | 
			
		||||
                            sb.Remove(sb.Length - 1, 1).Append("\r\n);\r\n");
 | 
			
		||||
                            //创建表的索引
 | 
			
		||||
                            foreach (var uk in tb.Indexes)
 | 
			
		||||
                            {
 | 
			
		||||
                                sb.Append("CREATE ");
 | 
			
		||||
                                if (uk.IsUnique) sb.Append("UNIQUE ");
 | 
			
		||||
                                sb.Append("INDEX ").Append(_commonUtils.QuoteSqlName(ReplaceIndexName(uk.Name, tbname[1]))).Append(" ON ").Append(createTableName).Append("(");
 | 
			
		||||
                                foreach (var tbcol in uk.Columns)
 | 
			
		||||
                                {
 | 
			
		||||
                                    sb.Append(_commonUtils.QuoteSqlName(tbcol.Column.Attribute.Name));
 | 
			
		||||
                                    if (tbcol.IsDesc) sb.Append(" DESC");
 | 
			
		||||
                                    sb.Append(", ");
 | 
			
		||||
                                }
 | 
			
		||||
                                sb.Remove(sb.Length - 2, 2).Append(");\r\n");
 | 
			
		||||
                            }
 | 
			
		||||
                            //备注
 | 
			
		||||
                            foreach (var tbcol in tb.ColumnsByPosition)
 | 
			
		||||
                            {
 | 
			
		||||
                                if (string.IsNullOrEmpty(tbcol.Comment) == false)
 | 
			
		||||
                                    AddOrUpdateMS_Description(sb, tbname[1], tbname[2], tbcol.Attribute.Name, tbcol.Comment);
 | 
			
		||||
                            }
 | 
			
		||||
                            if (string.IsNullOrEmpty(tb.Comment) == false)
 | 
			
		||||
                                AddOrUpdateMS_Description(sb, tbname[1], tbname[2], tb.Comment);
 | 
			
		||||
                            continue;
 | 
			
		||||
                        }
 | 
			
		||||
                        //如果新表,旧表在一个数据库和模式下,直接修改表名
 | 
			
		||||
                        if (string.Compare(tbname[0], tboldname[0], true) == 0 &&
 | 
			
		||||
                            string.Compare(tbname[1], tboldname[1], true) == 0)
 | 
			
		||||
                            sbalter.Append("use [").Append(tbname[0]).Append(_commonUtils.FormatSql("];\r\nEXEC sp_rename {0}, {1};\r\n", _commonUtils.QuoteSqlName(tboldname[0], tboldname[1], tboldname[2]), tbname[2]));
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            //如果新表,旧表不在一起,创建新表,导入数据,删除旧表
 | 
			
		||||
                            istmpatler = true;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                        tboldname = null; //如果新表已经存在,不走改表名逻辑
 | 
			
		||||
 | 
			
		||||
                    //对比字段,只可以修改类型、增加字段、有限的修改字段名;保证安全不删除字段
 | 
			
		||||
                    var sql = string.Format(@"
 | 
			
		||||
use [{0}];
 | 
			
		||||
select
 | 
			
		||||
a.name 'column'
 | 
			
		||||
,b.name + case 
 | 
			
		||||
 when b.name in ('char', 'varchar', 'nchar', 'nvarchar', 'binary', 'varbinary') then '(' + 
 | 
			
		||||
  case when a.max_length = -1 then 'MAX' 
 | 
			
		||||
  when b.name in ('nchar', 'nvarchar') then cast(a.max_length / 2 as varchar)
 | 
			
		||||
  else cast(a.max_length as varchar) end + ')'
 | 
			
		||||
 when b.name in ('numeric', 'decimal') then '(' + cast(a.precision as varchar) + ',' + cast(a.scale as varchar) + ')'
 | 
			
		||||
 else '' end as 'sqltype'
 | 
			
		||||
,case when a.is_nullable = 1 then '1' else '0' end 'isnullable'
 | 
			
		||||
,case when a.is_identity = 1 then '1' else '0' end 'isidentity'
 | 
			
		||||
,(select value from sys.extended_properties where major_id = a.object_id AND minor_id = a.column_id AND name = 'MS_Description') 'comment'
 | 
			
		||||
from sys.columns a
 | 
			
		||||
inner join sys.types b on b.user_type_id = a.user_type_id
 | 
			
		||||
left join sys.tables d on d.object_id = a.object_id
 | 
			
		||||
left join sys.schemas e on e.schema_id = d.schema_id
 | 
			
		||||
where a.object_id in (object_id(N'[{1}].[{2}]'));
 | 
			
		||||
use [" + database + "];", tboldname ?? tbname);
 | 
			
		||||
                    var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
 | 
			
		||||
                    var tbstruct = ds.ToDictionary(a => string.Concat(a[0]), a => new
 | 
			
		||||
                    {
 | 
			
		||||
                        column = string.Concat(a[0]),
 | 
			
		||||
                        sqlType = string.Concat(a[1]),
 | 
			
		||||
                        is_nullable = string.Concat(a[2]) == "1",
 | 
			
		||||
                        is_identity = string.Concat(a[3]) == "1",
 | 
			
		||||
                        comment = string.Concat(a[4])
 | 
			
		||||
                    }, StringComparer.CurrentCultureIgnoreCase);
 | 
			
		||||
 | 
			
		||||
                    if (istmpatler == false)
 | 
			
		||||
                    {
 | 
			
		||||
                        foreach (var tbcol in tb.ColumnsByPosition)
 | 
			
		||||
                        {
 | 
			
		||||
                            if (tbstruct.TryGetValue(tbcol.Attribute.Name, out var tbstructcol) ||
 | 
			
		||||
                                string.IsNullOrEmpty(tbcol.Attribute.OldName) == false && tbstruct.TryGetValue(tbcol.Attribute.OldName, out tbstructcol))
 | 
			
		||||
                            {
 | 
			
		||||
                                var isCommentChanged = tbstructcol.comment != (tbcol.Comment ?? "");
 | 
			
		||||
                                if (tbcol.Attribute.DbType.StartsWith(tbstructcol.sqlType, StringComparison.CurrentCultureIgnoreCase) == false ||
 | 
			
		||||
                                    tbcol.Attribute.IsNullable != tbstructcol.is_nullable ||
 | 
			
		||||
                                    tbcol.Attribute.IsIdentity != tbstructcol.is_identity)
 | 
			
		||||
                                {
 | 
			
		||||
                                    istmpatler = true;
 | 
			
		||||
                                    break;
 | 
			
		||||
                                }
 | 
			
		||||
                                if (string.Compare(tbstructcol.column, tbcol.Attribute.OldName, true) == 0)
 | 
			
		||||
                                    //修改列名
 | 
			
		||||
                                    sbalter.Append(_commonUtils.FormatSql("EXEC sp_rename {0}, {1}, 'COLUMN';\r\n", $"{tbname[0]}.{tbname[1]}.{tbname[2]}.{tbstructcol.column}", tbcol.Attribute.Name));
 | 
			
		||||
                                if (isCommentChanged)
 | 
			
		||||
                                    //修改备备注
 | 
			
		||||
                                    AddOrUpdateMS_Description(sbalter, tbname[1], tbname[2], tbcol.Attribute.Name, tbcol.Comment);
 | 
			
		||||
                                continue;
 | 
			
		||||
                            }
 | 
			
		||||
                            //添加列
 | 
			
		||||
                            sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName(tbname[0], tbname[1], tbname[2])).Append(" ADD ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ").Append(tbcol.Attribute.DbType);
 | 
			
		||||
                            if (tbcol.Attribute.IsIdentity == true && tbcol.Attribute.DbType.IndexOf("identity", StringComparison.CurrentCultureIgnoreCase) == -1) sbalter.Append(" identity(1,1)");
 | 
			
		||||
                            if (tbcol.Attribute.IsNullable == false && tbcol.DbDefaultValue != "NULL" && tbcol.Attribute.IsIdentity == false) sbalter.Append(" default(").Append(GetTransferDbDefaultValue(tbcol)).Append(")");
 | 
			
		||||
                            sbalter.Append(";\r\n");
 | 
			
		||||
                            if (string.IsNullOrEmpty(tbcol.Comment) == false) AddOrUpdateMS_Description(sbalter, tbname[1], tbname[2], tbcol.Attribute.Name, tbcol.Comment);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    if (istmpatler == false)
 | 
			
		||||
                    {
 | 
			
		||||
                        var dsuksql = string.Format(@"
 | 
			
		||||
use [{0}];
 | 
			
		||||
select 
 | 
			
		||||
c.name
 | 
			
		||||
,b.name
 | 
			
		||||
,case when a.is_descending_key = 1 then 1 else 0 end
 | 
			
		||||
,case when b.is_unique = 1 then 1 else 0 end
 | 
			
		||||
from sys.index_columns a
 | 
			
		||||
inner join sys.indexes b on b.object_id = a.object_id and b.index_id = a.index_id
 | 
			
		||||
left join sys.columns c on c.object_id = a.object_id and c.column_id = a.column_id
 | 
			
		||||
where a.object_id in (object_id(N'[{1}].[{2}]')) and b.is_primary_key = 0;
 | 
			
		||||
use [" + database + "];", tboldname ?? tbname);
 | 
			
		||||
                        var dsuk = _orm.Ado.ExecuteArray(CommandType.Text, dsuksql).Select(a => new[] { string.Concat(a[0]), string.Concat(a[1]), string.Concat(a[2]), string.Concat(a[3]) });
 | 
			
		||||
                        foreach (var uk in tb.Indexes)
 | 
			
		||||
                        {
 | 
			
		||||
                            if (string.IsNullOrEmpty(uk.Name) || uk.Columns.Any() == false) continue;
 | 
			
		||||
                            var ukname = ReplaceIndexName(uk.Name, tbname[1]);
 | 
			
		||||
                            var dsukfind1 = dsuk.Where(a => string.Compare(a[1], ukname, true) == 0).ToArray();
 | 
			
		||||
                            if (dsukfind1.Any() == false || dsukfind1.Length != uk.Columns.Length || dsukfind1.Where(a => (a[3] == "1") == uk.IsUnique && uk.Columns.Where(b => string.Compare(b.Column.Attribute.Name, a[0], true) == 0 && (a[2] == "1") == b.IsDesc).Any()).Count() != uk.Columns.Length)
 | 
			
		||||
                            {
 | 
			
		||||
                                if (dsukfind1.Any()) sbalter.Append("DROP INDEX ").Append(_commonUtils.QuoteSqlName(ukname)).Append(" ON ").Append(_commonUtils.QuoteSqlName(tbname[1], tbname[2])).Append(";\r\n");
 | 
			
		||||
                                sbalter.Append("CREATE ");
 | 
			
		||||
                                if (uk.IsUnique) sbalter.Append("UNIQUE ");
 | 
			
		||||
                                sbalter.Append("INDEX ").Append(_commonUtils.QuoteSqlName(ukname)).Append(" ON ").Append(_commonUtils.QuoteSqlName(tbname[1], tbname[2])).Append("(");
 | 
			
		||||
                                foreach (var tbcol in uk.Columns)
 | 
			
		||||
                                {
 | 
			
		||||
                                    sbalter.Append(_commonUtils.QuoteSqlName(tbcol.Column.Attribute.Name));
 | 
			
		||||
                                    if (tbcol.IsDesc) sbalter.Append(" DESC");
 | 
			
		||||
                                    sbalter.Append(", ");
 | 
			
		||||
                                }
 | 
			
		||||
                                sbalter.Remove(sbalter.Length - 2, 2).Append(");\r\n");
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    if (istmpatler == false)
 | 
			
		||||
                    {
 | 
			
		||||
                        var dbcommentsql = $" SELECT value from fn_listextendedproperty('MS_Description', 'schema', N'{tbname[1].Replace("'", "''")}', 'table', N'{tbname[2].Replace("'", "''")}', NULL, NULL)";
 | 
			
		||||
                        if (string.Compare(tbname[0], database, true) != 0) dbcommentsql = $"use [{tbname[0]}];{dbcommentsql};use [{database}];";
 | 
			
		||||
                        var dbcomment = string.Concat(_orm.Ado.ExecuteScalar(CommandType.Text, dbcommentsql));
 | 
			
		||||
                        if (dbcomment != (tb.Comment ?? ""))
 | 
			
		||||
                            AddOrUpdateMS_Description(sbalter, tbname[1], tbname[2], tb.Comment);
 | 
			
		||||
 | 
			
		||||
                        if (sbalter.Length > 0)
 | 
			
		||||
                            sb.Append($"use [{tbname[0]}];").Append(sbalter).Append("\r\nuse [").Append(database).Append("];");
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
                    //创建临时表,数据导进临时表,然后删除原表,将临时表改名为原表名
 | 
			
		||||
                    bool idents = false;
 | 
			
		||||
                    var tablename = tboldname == null ? _commonUtils.QuoteSqlName(tbname[0], tbname[1], tbname[2]) : _commonUtils.QuoteSqlName(tboldname[0], tboldname[1], tboldname[2]);
 | 
			
		||||
                    var tmptablename = _commonUtils.QuoteSqlName(tbname[0], tbname[1], $"FreeSqlTmp_{tbname[2]}");
 | 
			
		||||
                    sb.Append("BEGIN TRANSACTION\r\n")
 | 
			
		||||
                        .Append("SET QUOTED_IDENTIFIER ON\r\n")
 | 
			
		||||
                        .Append("SET ARITHABORT ON\r\n")
 | 
			
		||||
                        .Append("SET NUMERIC_ROUNDABORT OFF\r\n")
 | 
			
		||||
                        .Append("SET CONCAT_NULL_YIELDS_NULL ON\r\n")
 | 
			
		||||
                        .Append("SET ANSI_NULLS ON\r\n")
 | 
			
		||||
                        .Append("SET ANSI_PADDING ON\r\n")
 | 
			
		||||
                        .Append("SET ANSI_WARNINGS ON\r\n")
 | 
			
		||||
                        .Append("COMMIT\r\n");
 | 
			
		||||
                    sb.Append("BEGIN TRANSACTION;\r\n");
 | 
			
		||||
                    //创建临时表
 | 
			
		||||
                    sb.Append("CREATE TABLE ").Append(tmptablename).Append(" ( ");
 | 
			
		||||
                    var pkidx2 = 0;
 | 
			
		||||
                    foreach (var tbcol in tb.ColumnsByPosition)
 | 
			
		||||
                    {
 | 
			
		||||
                        sb.Append(" \r\n  ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ").Append(tbcol.Attribute.DbType);
 | 
			
		||||
                        if (tbcol.Attribute.IsIdentity == true && tbcol.Attribute.DbType.IndexOf("identity", StringComparison.CurrentCultureIgnoreCase) == -1) sb.Append(" identity(1,1)");
 | 
			
		||||
                        if (tbcol.Attribute.IsPrimary == true)
 | 
			
		||||
                        {
 | 
			
		||||
                            if (tb.Primarys.Length > 1)
 | 
			
		||||
                            {
 | 
			
		||||
                                if (pkidx2 == tb.Primarys.Length - 1)
 | 
			
		||||
                                    sb.Append(" primary key (").Append(string.Join(", ", tb.Primarys.Select(a => _commonUtils.QuoteSqlName(a.Attribute.Name)))).Append(")");
 | 
			
		||||
                            }
 | 
			
		||||
                            else
 | 
			
		||||
                                sb.Append(" primary key");
 | 
			
		||||
                            pkidx2++;
 | 
			
		||||
                        }
 | 
			
		||||
                        sb.Append(",");
 | 
			
		||||
                        idents = idents || tbcol.Attribute.IsIdentity == true;
 | 
			
		||||
                    }
 | 
			
		||||
                    sb.Remove(sb.Length - 1, 1).Append("\r\n);\r\n");
 | 
			
		||||
                    //备注
 | 
			
		||||
                    foreach (var tbcol in tb.ColumnsByPosition)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (string.IsNullOrEmpty(tbcol.Comment) == false)
 | 
			
		||||
                            AddOrUpdateMS_Description(sb, tbname[1], $"FreeSqlTmp_{tbname[2]}", tbcol.Attribute.Name, tbcol.Comment);
 | 
			
		||||
                    }
 | 
			
		||||
                    if (string.IsNullOrEmpty(tb.Comment) == false)
 | 
			
		||||
                        AddOrUpdateMS_Description(sb, tbname[1], $"FreeSqlTmp_{tbname[2]}", tb.Comment);
 | 
			
		||||
 | 
			
		||||
                    if ((_commonUtils as CustomSqlServerUtils).ServerVersion > 9) //SqlServer 2008+
 | 
			
		||||
                        sb.Append("ALTER TABLE ").Append(tmptablename).Append(" SET (LOCK_ESCALATION = TABLE);\r\n");
 | 
			
		||||
                    if (idents) sb.Append("SET IDENTITY_INSERT ").Append(tmptablename).Append(" ON;\r\n");
 | 
			
		||||
                    sb.Append("IF EXISTS(SELECT 1 FROM ").Append(tablename).Append(")\r\n");
 | 
			
		||||
                    sb.Append("\tEXEC('INSERT INTO ").Append(tmptablename).Append(" (");
 | 
			
		||||
                    foreach (var tbcol in tb.ColumnsByPosition)
 | 
			
		||||
                        sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
 | 
			
		||||
                    sb.Remove(sb.Length - 2, 2).Append(")\r\n\t\tSELECT ");
 | 
			
		||||
                    foreach (var tbcol in tb.ColumnsByPosition)
 | 
			
		||||
                    {
 | 
			
		||||
                        var insertvalue = "NULL";
 | 
			
		||||
                        if (tbstruct.TryGetValue(tbcol.Attribute.Name, out var tbstructcol) ||
 | 
			
		||||
                            string.IsNullOrEmpty(tbcol.Attribute.OldName) == false && tbstruct.TryGetValue(tbcol.Attribute.OldName, out tbstructcol))
 | 
			
		||||
                        {
 | 
			
		||||
                            insertvalue = _commonUtils.QuoteSqlName(tbstructcol.column);
 | 
			
		||||
                            if (tbcol.Attribute.DbType.StartsWith(tbstructcol.sqlType, StringComparison.CurrentCultureIgnoreCase) == false)
 | 
			
		||||
                                insertvalue = $"cast({insertvalue} as {tbcol.Attribute.DbType.Split(' ').First()})";
 | 
			
		||||
                            if (tbcol.Attribute.IsNullable != tbstructcol.is_nullable)
 | 
			
		||||
                                insertvalue = $"isnull({insertvalue},{GetTransferDbDefaultValue(tbcol)})";
 | 
			
		||||
                        }
 | 
			
		||||
                        else if (tbcol.Attribute.IsNullable == false)
 | 
			
		||||
                            if (tbcol.DbDefaultValue != "NULL" && tbcol.Attribute.IsIdentity == false)
 | 
			
		||||
                                insertvalue = GetTransferDbDefaultValue(tbcol);
 | 
			
		||||
                        sb.Append(insertvalue.Replace("'", "''")).Append(", ");
 | 
			
		||||
                    }
 | 
			
		||||
                    sb.Remove(sb.Length - 2, 2).Append(" FROM ").Append(tablename).Append(" WITH (HOLDLOCK TABLOCKX)');\r\n");
 | 
			
		||||
                    if (idents) sb.Append("SET IDENTITY_INSERT ").Append(tmptablename).Append(" OFF;\r\n");
 | 
			
		||||
                    sb.Append("DROP TABLE ").Append(tablename).Append(";\r\n");
 | 
			
		||||
                    sb.Append("EXECUTE sp_rename N'").Append(tmptablename).Append("', N'").Append(tbname[2]).Append("', 'OBJECT';\r\n");
 | 
			
		||||
                    //创建表的索引
 | 
			
		||||
                    foreach (var uk in tb.Indexes)
 | 
			
		||||
                    {
 | 
			
		||||
                        sb.Append("CREATE ");
 | 
			
		||||
                        if (uk.IsUnique) sb.Append("UNIQUE ");
 | 
			
		||||
                        sb.Append("INDEX ").Append(_commonUtils.QuoteSqlName(ReplaceIndexName(uk.Name, tbname[1]))).Append(" ON ").Append(tablename).Append("(");
 | 
			
		||||
                        foreach (var tbcol in uk.Columns)
 | 
			
		||||
                        {
 | 
			
		||||
                            sb.Append(_commonUtils.QuoteSqlName(tbcol.Column.Attribute.Name));
 | 
			
		||||
                            if (tbcol.IsDesc) sb.Append(" DESC");
 | 
			
		||||
                            sb.Append(", ");
 | 
			
		||||
                        }
 | 
			
		||||
                        sb.Remove(sb.Length - 2, 2).Append(");\r\n");
 | 
			
		||||
                    }
 | 
			
		||||
                    sb.Append("COMMIT;\r\n");
 | 
			
		||||
                }
 | 
			
		||||
                return sb.Length == 0 ? null : sb.ToString();
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    if (string.IsNullOrEmpty(database) == false)
 | 
			
		||||
                        conn.Value.ChangeDatabase(database);
 | 
			
		||||
                    _orm.Ado.MasterPool.Return(conn);
 | 
			
		||||
                }
 | 
			
		||||
                catch
 | 
			
		||||
                {
 | 
			
		||||
                    _orm.Ado.MasterPool.Return(conn, true);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            object LocalExecuteScalar(string db, string sql)
 | 
			
		||||
            {
 | 
			
		||||
                if (string.Compare(database, db) != 0) conn.Value.ChangeDatabase(db);
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    using (var cmd = conn.Value.CreateCommand())
 | 
			
		||||
                    {
 | 
			
		||||
                        cmd.CommandText = sql;
 | 
			
		||||
                        cmd.CommandType = CommandType.Text;
 | 
			
		||||
                        return cmd.ExecuteScalar();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                finally
 | 
			
		||||
                {
 | 
			
		||||
                    if (string.Compare(database, db) != 0) conn.Value.ChangeDatabase(database);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        string GetTransferDbDefaultValue(ColumnInfo col)
 | 
			
		||||
        {
 | 
			
		||||
            var ddv = col.DbDefaultValue;
 | 
			
		||||
            if (string.IsNullOrEmpty(ddv) || ddv == "NULL") return ddv;
 | 
			
		||||
            if (col.Attribute.MapType.NullableTypeOrThis() == typeof(DateTime) && DateTime.TryParse(ddv, out var trydt))
 | 
			
		||||
            {
 | 
			
		||||
                if (col.Attribute.DbType.Contains("SMALLDATETIME") && trydt < new DateTime(1900, 1, 1)) ddv = _commonUtils.FormatSql("{0}", new DateTime(1900, 1, 1));
 | 
			
		||||
                else if (col.Attribute.DbType.Contains("DATETIME") && trydt < new DateTime(1753, 1, 1)) ddv = _commonUtils.FormatSql("{0}", new DateTime(1753, 1, 1));
 | 
			
		||||
                else if (col.Attribute.DbType.Contains("DATE") && trydt < new DateTime(0001, 1, 1)) ddv = _commonUtils.FormatSql("{0}", new DateTime(0001, 1, 1));
 | 
			
		||||
            }
 | 
			
		||||
            return ddv;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,521 @@
 | 
			
		||||
using FreeSql.DatabaseModel;
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using FreeSql.Internal.Model;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Data;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Custom.SqlServer
 | 
			
		||||
{
 | 
			
		||||
    class CustomSqlServerDbFirst : IDbFirst
 | 
			
		||||
    {
 | 
			
		||||
        IFreeSql _orm;
 | 
			
		||||
        protected CommonUtils _commonUtils;
 | 
			
		||||
        protected CommonExpression _commonExpression;
 | 
			
		||||
        public CustomSqlServerDbFirst(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression)
 | 
			
		||||
        {
 | 
			
		||||
            _orm = orm;
 | 
			
		||||
            _commonUtils = commonUtils;
 | 
			
		||||
            _commonExpression = commonExpression;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public int GetDbType(DbColumnInfo column) => (int)GetSqlDbType(column);
 | 
			
		||||
        DbType GetSqlDbType(DbColumnInfo column)
 | 
			
		||||
        {
 | 
			
		||||
            switch (column.DbTypeText?.ToLower())
 | 
			
		||||
            {
 | 
			
		||||
                case "bit": return DbType.Boolean;
 | 
			
		||||
                case "tinyint": return DbType.Byte;
 | 
			
		||||
                case "smallint": return DbType.Int16;
 | 
			
		||||
                case "int": return DbType.Int32;
 | 
			
		||||
                case "bigint": return DbType.Int64;
 | 
			
		||||
                case "numeric":
 | 
			
		||||
                case "decimal": return DbType.Decimal;
 | 
			
		||||
                case "smallmoney": return DbType.Decimal;
 | 
			
		||||
                case "money": return DbType.Decimal;
 | 
			
		||||
                case "float": return DbType.Double;
 | 
			
		||||
                case "real": return DbType.Single;
 | 
			
		||||
                case "date": return DbType.Date;
 | 
			
		||||
                case "datetime": return DbType.DateTime;
 | 
			
		||||
                case "datetime2": return DbType.DateTime2;
 | 
			
		||||
                case "datetimeoffset": return DbType.DateTime;
 | 
			
		||||
                case "smalldatetime": return DbType.DateTime;
 | 
			
		||||
                case "time": return DbType.Time;
 | 
			
		||||
                case "char": return DbType.AnsiString;
 | 
			
		||||
                case "varchar": return DbType.String;
 | 
			
		||||
                case "text": return DbType.String;
 | 
			
		||||
                case "nchar": return DbType.String;
 | 
			
		||||
                case "nvarchar": return DbType.String;
 | 
			
		||||
                case "ntext": return DbType.String;
 | 
			
		||||
                case "binary": return DbType.Binary;
 | 
			
		||||
                case "varbinary": return DbType.Binary;
 | 
			
		||||
                case "image": return DbType.Binary;
 | 
			
		||||
                case "timestamp": return DbType.Binary;
 | 
			
		||||
                case "uniqueidentifier": return DbType.Guid;
 | 
			
		||||
                default: return DbType.String;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        static readonly Dictionary<int, DbToCs> _dicDbToCs = new Dictionary<int, DbToCs>() {
 | 
			
		||||
                { (int)DbType.Boolean, new DbToCs("(bool?)", "{0} == \"1\"", "{0} == true ? \"1\" : \"0\"", "bool?", typeof(bool), typeof(bool?), "{0}.Value", "GetBoolean") },
 | 
			
		||||
 | 
			
		||||
                { (int)DbType.SByte, new DbToCs("(sbyte?)", "sbyte.Parse({0})", "{0}.ToString()", "sbyte?", typeof(sbyte), typeof(sbyte?), "{0}.Value", "GetInt16") },
 | 
			
		||||
                { (int)DbType.Int16, new DbToCs("(short?)", "short.Parse({0})", "{0}.ToString()", "short?", typeof(short), typeof(short?), "{0}.Value", "GetInt16") },
 | 
			
		||||
                { (int)DbType.Int32, new DbToCs("(int?)", "int.Parse({0})", "{0}.ToString()", "int?", typeof(int), typeof(int?), "{0}.Value", "GetInt32") },
 | 
			
		||||
                { (int)DbType.Int64, new DbToCs("(long?)", "long.Parse({0})", "{0}.ToString()", "long?", typeof(long), typeof(long?), "{0}.Value", "GetInt64") },
 | 
			
		||||
 | 
			
		||||
                { (int)DbType.Byte, new DbToCs("(byte?)", "byte.Parse({0})", "{0}.ToString()", "byte?", typeof(byte), typeof(byte?), "{0}.Value", "GetByte") },
 | 
			
		||||
                { (int)DbType.UInt16, new DbToCs("(ushort?)", "ushort.Parse({0})", "{0}.ToString()", "ushort?", typeof(ushort), typeof(ushort?), "{0}.Value", "GetInt32") },
 | 
			
		||||
                { (int)DbType.UInt32, new DbToCs("(uint?)", "uint.Parse({0})", "{0}.ToString()", "uint?", typeof(uint), typeof(uint?), "{0}.Value", "GetInt64") },
 | 
			
		||||
                { (int)DbType.UInt64, new DbToCs("(ulong?)", "ulong.Parse({0})", "{0}.ToString()", "ulong?", typeof(ulong), typeof(ulong?), "{0}.Value", "GetInt64") },
 | 
			
		||||
 | 
			
		||||
                { (int)DbType.Decimal, new DbToCs("(decimal?)", "decimal.Parse({0})", "{0}.ToString()", "decimal?", typeof(decimal), typeof(decimal?), "{0}.Value", "GetDecimal") },
 | 
			
		||||
                { (int)DbType.Double, new DbToCs("(double?)", "double.Parse({0})", "{0}.ToString()", "double?", typeof(double), typeof(double?), "{0}.Value", "GetDouble") },
 | 
			
		||||
                { (int)DbType.Single, new DbToCs("(float?)", "float.Parse({0})", "{0}.ToString()", "float?", typeof(float), typeof(float?), "{0}.Value", "GetFloat") },
 | 
			
		||||
 | 
			
		||||
                { (int)DbType.Time, new DbToCs("(TimeSpan?)", "TimeSpan.Parse(double.Parse({0}))", "{0}.Ticks.ToString()", "TimeSpan?", typeof(TimeSpan), typeof(TimeSpan?), "{0}.Value", "GetValue") },
 | 
			
		||||
                { (int)DbType.Date, new DbToCs("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") },
 | 
			
		||||
                { (int)DbType.DateTime, new DbToCs("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") },
 | 
			
		||||
                { (int)DbType.DateTime2, new DbToCs("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") },
 | 
			
		||||
 | 
			
		||||
                { (int)DbType.Binary, new DbToCs("(byte[])", "Convert.FromBase64String({0})", "Convert.ToBase64String({0})", "byte[]", typeof(byte[]), typeof(byte[]), "{0}", "GetValue") },
 | 
			
		||||
 | 
			
		||||
                { (int)DbType.AnsiString, new DbToCs("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
 | 
			
		||||
                { (int)DbType.String, new DbToCs("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
 | 
			
		||||
 | 
			
		||||
                { (int)DbType.Guid, new DbToCs("(Guid?)", "Guid.Parse({0})", "{0}.ToString()", "Guid?", typeof(Guid), typeof(Guid?), "{0}.Value", "GetGuid") },
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
        public string GetCsConvert(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? (column.IsNullable ? trydc.csConvert : trydc.csConvert.Replace("?", "")) : null;
 | 
			
		||||
        public string GetCsParse(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.csParse : null;
 | 
			
		||||
        public string GetCsStringify(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.csStringify : null;
 | 
			
		||||
        public string GetCsType(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? (column.IsNullable ? trydc.csType : trydc.csType.Replace("?", "")) : null;
 | 
			
		||||
        public Type GetCsTypeInfo(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.csTypeInfo : null;
 | 
			
		||||
        public string GetCsTypeValue(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.csTypeValue : null;
 | 
			
		||||
        public string GetDataReaderMethod(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.dataReaderMethod : null;
 | 
			
		||||
 | 
			
		||||
        public List<string> GetDatabases()
 | 
			
		||||
        {
 | 
			
		||||
            var sql = @" select name from sys.databases where name not in ('master','tempdb','model','msdb')";
 | 
			
		||||
            var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
 | 
			
		||||
            return ds.Select(a => a.FirstOrDefault()?.ToString()).ToList();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool ExistsTable(string name, bool ignoreCase)
 | 
			
		||||
        {
 | 
			
		||||
            if (string.IsNullOrEmpty(name)) return false;
 | 
			
		||||
            var olddatabase = "";
 | 
			
		||||
            using (var conn = _orm.Ado.MasterPool.Get(TimeSpan.FromSeconds(5)))
 | 
			
		||||
            {
 | 
			
		||||
                olddatabase = conn.Value.Database;
 | 
			
		||||
            }
 | 
			
		||||
            var tbname = _commonUtils.SplitTableName(name);
 | 
			
		||||
            if (tbname?.Length == 1) tbname = new[] { olddatabase, "dbo", tbname[0] };
 | 
			
		||||
            if (tbname?.Length == 2) tbname = new[] { olddatabase, tbname[0], tbname[1] };
 | 
			
		||||
            tbname = tbname.Select(a => a.ToLower()).ToArray();
 | 
			
		||||
            var sql = $@"
 | 
			
		||||
use [{tbname[0]}];
 | 
			
		||||
select 
 | 
			
		||||
1
 | 
			
		||||
from sys.tables a
 | 
			
		||||
inner join sys.schemas b on b.schema_id = a.schema_id
 | 
			
		||||
where lower(b.name)={_commonUtils.FormatSql("{0}", tbname[1])} and lower(a.name)={_commonUtils.FormatSql("{0}", tbname[2])}
 | 
			
		||||
;
 | 
			
		||||
use [{olddatabase}];
 | 
			
		||||
";
 | 
			
		||||
            return string.Concat(_orm.Ado.ExecuteScalar(CommandType.Text, sql)) == "1";
 | 
			
		||||
        }
 | 
			
		||||
        public DbTableInfo GetTableByName(string name, bool ignoreCase = true) => GetTables(null, name, ignoreCase)?.FirstOrDefault();
 | 
			
		||||
        public List<DbTableInfo> GetTablesByDatabase(params string[] database) => GetTables(database, null, false);
 | 
			
		||||
 | 
			
		||||
        public List<DbTableInfo> GetTables(string[] database, string tablename, bool ignoreCase)
 | 
			
		||||
        {
 | 
			
		||||
            var olddatabase = "";
 | 
			
		||||
            using (var conn = _orm.Ado.MasterPool.Get(TimeSpan.FromSeconds(5)))
 | 
			
		||||
            {
 | 
			
		||||
                olddatabase = conn.Value.Database;
 | 
			
		||||
            }
 | 
			
		||||
            string[] tbname = null;
 | 
			
		||||
            string[] dbs = database == null || database.Any() == false ? new[] { olddatabase } : database;
 | 
			
		||||
            if (string.IsNullOrEmpty(tablename) == false)
 | 
			
		||||
            {
 | 
			
		||||
                tbname = _commonUtils.SplitTableName(tablename);
 | 
			
		||||
                if (tbname?.Length == 1) tbname = new[] { olddatabase, "dbo", tbname[0] };
 | 
			
		||||
                if (tbname?.Length == 2) tbname = new[] { olddatabase, tbname[0], tbname[1] };
 | 
			
		||||
                tbname = tbname.Select(a => a.ToLower()).ToArray();
 | 
			
		||||
                dbs = new[] { tbname[0] };
 | 
			
		||||
            }
 | 
			
		||||
            var tables = new List<DbTableInfo>();
 | 
			
		||||
 | 
			
		||||
            foreach (var db in dbs)
 | 
			
		||||
            {
 | 
			
		||||
                if (string.IsNullOrEmpty(db)) continue;
 | 
			
		||||
 | 
			
		||||
                var loc1 = new List<DbTableInfo>();
 | 
			
		||||
                var loc2 = new Dictionary<int, DbTableInfo>();
 | 
			
		||||
                var loc3 = new Dictionary<int, Dictionary<string, DbColumnInfo>>();
 | 
			
		||||
 | 
			
		||||
                var sql = $@"
 | 
			
		||||
use [{db}];
 | 
			
		||||
select * from (
 | 
			
		||||
select 
 | 
			
		||||
 a.object_id
 | 
			
		||||
,b.name 'owner'
 | 
			
		||||
,a.name 'name'
 | 
			
		||||
,(select value from sys.extended_properties where major_id = a.object_id AND minor_id = 0 AND name = 'MS_Description') 'comment'
 | 
			
		||||
,'TABLE' type
 | 
			
		||||
from sys.tables a
 | 
			
		||||
inner join sys.schemas b on b.schema_id = a.schema_id
 | 
			
		||||
where not(b.name = 'dbo' and a.name = 'sysdiagrams')
 | 
			
		||||
union all
 | 
			
		||||
select
 | 
			
		||||
 a.object_id
 | 
			
		||||
,b.name 'owner'
 | 
			
		||||
,a.name 'name'
 | 
			
		||||
,(select value from sys.extended_properties where major_id = a.object_id AND minor_id = 0 AND name = 'MS_Description') 'comment'
 | 
			
		||||
,'VIEW' type
 | 
			
		||||
from sys.views a
 | 
			
		||||
inner join sys.schemas b on b.schema_id = a.schema_id
 | 
			
		||||
union all
 | 
			
		||||
select 
 | 
			
		||||
 a.object_id
 | 
			
		||||
,b.name 'owner'
 | 
			
		||||
,a.name 'name'
 | 
			
		||||
,(select value from sys.extended_properties where major_id = a.object_id AND minor_id = 0 AND name = 'MS_Description') 'comment'
 | 
			
		||||
,'StoreProcedure' type
 | 
			
		||||
from sys.procedures a
 | 
			
		||||
inner join sys.schemas b on b.schema_id = a.schema_id
 | 
			
		||||
where a.type = 'P' and charindex('diagram', a.name) = 0
 | 
			
		||||
) ft_dbf{(tbname == null ? "" : _commonUtils.FormatSql(" where lower([owner])={0} and lower([name])={1}", new[] { tbname[1], tbname[2] }))}
 | 
			
		||||
order by type desc, [owner], [name];
 | 
			
		||||
use [{olddatabase}];
 | 
			
		||||
";
 | 
			
		||||
                var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
 | 
			
		||||
                if (ds == null) return loc1;
 | 
			
		||||
 | 
			
		||||
                var loc6 = new List<int[]>();
 | 
			
		||||
                var loc66 = new List<int[]>();
 | 
			
		||||
                var loc6_1000 = new List<int>();
 | 
			
		||||
                var loc66_1000 = new List<int>();
 | 
			
		||||
                foreach (object[] row in ds)
 | 
			
		||||
                {
 | 
			
		||||
                    int object_id = int.Parse(string.Concat(row[0]));
 | 
			
		||||
                    var owner = string.Concat(row[1]);
 | 
			
		||||
                    var table = string.Concat(row[2]);
 | 
			
		||||
                    var comment = string.Concat(row[3]);
 | 
			
		||||
                    Enum.TryParse<DbTableType>(string.Concat(row[4]), out var type);
 | 
			
		||||
                    loc2.Add(object_id, new DbTableInfo { Id = object_id.ToString(), Schema = owner, Name = table, Comment = comment, Type = type });
 | 
			
		||||
                    loc3.Add(object_id, new Dictionary<string, DbColumnInfo>());
 | 
			
		||||
                    switch (type)
 | 
			
		||||
                    {
 | 
			
		||||
                        case DbTableType.VIEW:
 | 
			
		||||
                        case DbTableType.TABLE:
 | 
			
		||||
                            loc6_1000.Add(object_id);
 | 
			
		||||
                            if (loc6_1000.Count >= 500)
 | 
			
		||||
                            {
 | 
			
		||||
                                loc6.Add(loc6_1000.ToArray());
 | 
			
		||||
                                loc6_1000.Clear();
 | 
			
		||||
                            }
 | 
			
		||||
                            break;
 | 
			
		||||
                        case DbTableType.StoreProcedure:
 | 
			
		||||
                            loc66_1000.Add(object_id);
 | 
			
		||||
                            if (loc66_1000.Count >= 500)
 | 
			
		||||
                            {
 | 
			
		||||
                                loc66.Add(loc66_1000.ToArray());
 | 
			
		||||
                                loc66_1000.Clear();
 | 
			
		||||
                            }
 | 
			
		||||
                            break;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                if (loc6_1000.Count > 0) loc6.Add(loc6_1000.ToArray());
 | 
			
		||||
                if (loc66_1000.Count > 0) loc66.Add(loc66_1000.ToArray());
 | 
			
		||||
 | 
			
		||||
                if (loc6.Count == 0) return loc1;
 | 
			
		||||
                Func<List<int[]>, StringBuilder> getloc8Sb = loclist =>
 | 
			
		||||
                {
 | 
			
		||||
                    if (loclist.Count == 0) return new StringBuilder();
 | 
			
		||||
                    var loc8sb = new StringBuilder().Append("(");
 | 
			
		||||
                    for (var loc8sbidx = 0; loc8sbidx < loclist.Count; loc8sbidx++)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (loc8sbidx > 0) loc8sb.Append(" OR ");
 | 
			
		||||
                        loc8sb.Append("a.table_name in (");
 | 
			
		||||
                        for (var loc8sbidx2 = 0; loc8sbidx2 < loclist[loc8sbidx].Length; loc8sbidx2++)
 | 
			
		||||
                        {
 | 
			
		||||
                            if (loc8sbidx2 > 0) loc8sb.Append(",");
 | 
			
		||||
                            loc8sb.Append(loclist[loc8sbidx][loc8sbidx2]);
 | 
			
		||||
                        }
 | 
			
		||||
                        loc8sb.Append(")");
 | 
			
		||||
                    }
 | 
			
		||||
                    loc8sb.Append(")");
 | 
			
		||||
                    return loc8sb;
 | 
			
		||||
                };
 | 
			
		||||
                var loc8 = getloc8Sb(loc6);
 | 
			
		||||
                var loc88 = getloc8Sb(loc66);
 | 
			
		||||
 | 
			
		||||
                var tsql_place = @"
 | 
			
		||||
 | 
			
		||||
select 
 | 
			
		||||
isnull(e.name,'') + '.' + isnull(d.name,'')
 | 
			
		||||
,a.object_id
 | 
			
		||||
,a.name 'column'
 | 
			
		||||
,b.name 'type'
 | 
			
		||||
,case
 | 
			
		||||
 when b.name in ('text', 'ntext', 'image') then -1
 | 
			
		||||
 when b.name in ('nchar', 'nvarchar') then a.max_length / 2
 | 
			
		||||
 else a.max_length end 'length'
 | 
			
		||||
,b.name + case 
 | 
			
		||||
 when b.name in ('char', 'varchar', 'nchar', 'nvarchar', 'binary', 'varbinary') then '(' + 
 | 
			
		||||
  case when a.max_length = -1 then 'MAX' 
 | 
			
		||||
  when b.name in ('nchar', 'nvarchar') then cast(a.max_length / 2 as varchar)
 | 
			
		||||
  else cast(a.max_length as varchar) end + ')'
 | 
			
		||||
 when b.name in ('numeric', 'decimal') then '(' + cast(a.precision as varchar) + ',' + cast(a.scale as varchar) + ')'
 | 
			
		||||
 else '' end as 'sqltype'
 | 
			
		||||
,( select value from sys.extended_properties where major_id = a.object_id AND minor_id = a.column_id AND name = 'MS_Description') 'comment'
 | 
			
		||||
{0} a
 | 
			
		||||
inner join sys.types b on b.user_type_id = a.user_type_id
 | 
			
		||||
left join sys.tables d on d.object_id = a.object_id
 | 
			
		||||
left join sys.schemas e on e.schema_id = d.schema_id{2}
 | 
			
		||||
where {1}
 | 
			
		||||
";
 | 
			
		||||
                sql = string.Format(tsql_place, @"
 | 
			
		||||
,a.is_nullable 'isnullable'
 | 
			
		||||
,a.is_identity 'isidentity'
 | 
			
		||||
,f.text as 'defaultvalue'
 | 
			
		||||
from sys.columns", loc8.ToString().Replace("a.table_name", "a.object_id"), @"
 | 
			
		||||
left join syscomments f on f.id = a.default_object_id
 | 
			
		||||
");
 | 
			
		||||
                if (loc88.Length > 0)
 | 
			
		||||
                {
 | 
			
		||||
                    sql += "union all" +
 | 
			
		||||
                    string.Format(tsql_place.Replace(
 | 
			
		||||
                        " select value from sys.extended_properties where major_id = a.object_id AND minor_id = a.column_id",
 | 
			
		||||
                        " select value from sys.extended_properties where major_id = a.object_id AND minor_id = a.parameter_id"), @"
 | 
			
		||||
,cast(0 as bit) 'isnullable'
 | 
			
		||||
,a.is_output 'isidentity'
 | 
			
		||||
,'' as 'defaultvalue'
 | 
			
		||||
from sys.parameters", loc88.ToString().Replace("a.table_name", "a.object_id"), "");
 | 
			
		||||
                }
 | 
			
		||||
                sql = $"use [{db}];{sql};use [{olddatabase}]; ";
 | 
			
		||||
                ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
 | 
			
		||||
                if (ds == null) return loc1;
 | 
			
		||||
 | 
			
		||||
                var position = 0;
 | 
			
		||||
                foreach (object[] row in ds)
 | 
			
		||||
                {
 | 
			
		||||
                    var table_id = string.Concat(row[0]);
 | 
			
		||||
                    var object_id = int.Parse(string.Concat(row[1]));
 | 
			
		||||
                    var column = string.Concat(row[2]);
 | 
			
		||||
                    var type = string.Concat(row[3]);
 | 
			
		||||
                    var max_length = int.Parse(string.Concat(row[4]));
 | 
			
		||||
                    var sqlType = string.Concat(row[5]);
 | 
			
		||||
                    var comment = string.Concat(row[6]);
 | 
			
		||||
                    var is_nullable = bool.Parse(string.Concat(row[7]));
 | 
			
		||||
                    var is_identity = bool.Parse(string.Concat(row[8]));
 | 
			
		||||
                    var defaultValue = string.Concat(row[9]);
 | 
			
		||||
                    if (max_length == 0) max_length = -1;
 | 
			
		||||
 | 
			
		||||
                    loc3[object_id].Add(column, new DbColumnInfo
 | 
			
		||||
                    {
 | 
			
		||||
                        Name = column,
 | 
			
		||||
                        MaxLength = max_length,
 | 
			
		||||
                        IsIdentity = is_identity,
 | 
			
		||||
                        IsNullable = is_nullable,
 | 
			
		||||
                        IsPrimary = false,
 | 
			
		||||
                        DbTypeText = type,
 | 
			
		||||
                        DbTypeTextFull = sqlType,
 | 
			
		||||
                        Table = loc2[object_id],
 | 
			
		||||
                        Comment = comment,
 | 
			
		||||
                        DefaultValue = defaultValue,
 | 
			
		||||
                        Position = ++position
 | 
			
		||||
                    });
 | 
			
		||||
                    loc3[object_id][column].DbType = this.GetDbType(loc3[object_id][column]);
 | 
			
		||||
                    loc3[object_id][column].CsType = this.GetCsTypeInfo(loc3[object_id][column]);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                sql = $@"
 | 
			
		||||
use [{db}];
 | 
			
		||||
select 
 | 
			
		||||
 a.object_id 'object_id'
 | 
			
		||||
,c.name 'column'
 | 
			
		||||
,b.name 'index_id'
 | 
			
		||||
,b.is_unique 'isunique'
 | 
			
		||||
,b.is_primary_key 'isprimarykey'
 | 
			
		||||
,cast(case when b.type_desc = 'CLUSTERED' then 1 else 0 end as bit) 'isclustered'
 | 
			
		||||
,case when a.is_descending_key = 1 then 1 else 0 end 'isdesc'
 | 
			
		||||
from sys.index_columns a
 | 
			
		||||
inner join sys.indexes b on b.object_id = a.object_id and b.index_id = a.index_id
 | 
			
		||||
left join sys.columns c on c.object_id = a.object_id and c.column_id = a.column_id
 | 
			
		||||
where {loc8.ToString().Replace("a.table_name", "a.object_id")}
 | 
			
		||||
;
 | 
			
		||||
use [{olddatabase}];
 | 
			
		||||
";
 | 
			
		||||
                ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
 | 
			
		||||
                if (ds == null) return loc1;
 | 
			
		||||
 | 
			
		||||
                var indexColumns = new Dictionary<int, Dictionary<string, DbIndexInfo>>();
 | 
			
		||||
                var uniqueColumns = new Dictionary<int, Dictionary<string, DbIndexInfo>>();
 | 
			
		||||
                foreach (object[] row in ds)
 | 
			
		||||
                {
 | 
			
		||||
                    int object_id = int.Parse(string.Concat(row[0]));
 | 
			
		||||
                    string column = string.Concat(row[1]);
 | 
			
		||||
                    string index_id = string.Concat(row[2]);
 | 
			
		||||
                    bool is_unique = bool.Parse(string.Concat(row[3]));
 | 
			
		||||
                    bool is_primary_key = bool.Parse(string.Concat(row[4]));
 | 
			
		||||
                    bool is_clustered = bool.Parse(string.Concat(row[5]));
 | 
			
		||||
                    bool is_desc = string.Concat(row[6]) == "1";
 | 
			
		||||
 | 
			
		||||
                    if (loc3.ContainsKey(object_id) == false || loc3[object_id].ContainsKey(column) == false) continue;
 | 
			
		||||
                    DbColumnInfo loc9 = loc3[object_id][column];
 | 
			
		||||
                    if (loc9.IsPrimary == false && is_primary_key) loc9.IsPrimary = is_primary_key;
 | 
			
		||||
 | 
			
		||||
                    Dictionary<string, DbIndexInfo> loc10 = null;
 | 
			
		||||
                    DbIndexInfo loc11 = null;
 | 
			
		||||
                    if (!indexColumns.TryGetValue(object_id, out loc10))
 | 
			
		||||
                        indexColumns.Add(object_id, loc10 = new Dictionary<string, DbIndexInfo>());
 | 
			
		||||
                    if (!loc10.TryGetValue(index_id, out loc11))
 | 
			
		||||
                        loc10.Add(index_id, loc11 = new DbIndexInfo());
 | 
			
		||||
                    loc11.Columns.Add(new DbIndexColumnInfo { Column = loc9, IsDesc = is_desc });
 | 
			
		||||
                    if (is_unique && !is_primary_key)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (!uniqueColumns.TryGetValue(object_id, out loc10))
 | 
			
		||||
                            uniqueColumns.Add(object_id, loc10 = new Dictionary<string, DbIndexInfo>());
 | 
			
		||||
                        if (!loc10.TryGetValue(index_id, out loc11))
 | 
			
		||||
                            loc10.Add(index_id, loc11 = new DbIndexInfo());
 | 
			
		||||
                        loc11.Columns.Add(new DbIndexColumnInfo { Column = loc9, IsDesc = is_desc });
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                foreach (var object_id in indexColumns.Keys)
 | 
			
		||||
                {
 | 
			
		||||
                    foreach (var column in indexColumns[object_id])
 | 
			
		||||
                        loc2[object_id].IndexesDict.Add(column.Key, column.Value);
 | 
			
		||||
                }
 | 
			
		||||
                foreach (var object_id in uniqueColumns.Keys)
 | 
			
		||||
                {
 | 
			
		||||
                    foreach (var column in uniqueColumns[object_id])
 | 
			
		||||
                    {
 | 
			
		||||
                        column.Value.Columns.Sort((c1, c2) => c1.Column.Name.CompareTo(c2.Column.Name));
 | 
			
		||||
                        loc2[object_id].UniquesDict.Add(column.Key, column.Value);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (tbname == null)
 | 
			
		||||
                {
 | 
			
		||||
                    sql = $@"
 | 
			
		||||
use [{db}];
 | 
			
		||||
select 
 | 
			
		||||
 b.object_id 'object_id'
 | 
			
		||||
,c.name 'column'
 | 
			
		||||
,e.name 'fkid'
 | 
			
		||||
,a.referenced_object_id
 | 
			
		||||
,cast(1 as bit) 'isforeignkey'
 | 
			
		||||
,d.name 'referenced_column'
 | 
			
		||||
,null 'referenced_sln'
 | 
			
		||||
,null 'referenced_table'
 | 
			
		||||
from sys.foreign_key_columns a
 | 
			
		||||
inner join sys.tables b on b.object_id = a.parent_object_id
 | 
			
		||||
inner join sys.columns c on c.object_id = a.parent_object_id and c.column_id = a.parent_column_id
 | 
			
		||||
inner join sys.columns d on d.object_id = a.referenced_object_id and d.column_id = a.referenced_column_id
 | 
			
		||||
left join sys.foreign_keys e on e.object_id = a.constraint_object_id
 | 
			
		||||
where {loc8.ToString().Replace("a.table_name", "b.object_id")}
 | 
			
		||||
;
 | 
			
		||||
use [{olddatabase}];
 | 
			
		||||
";
 | 
			
		||||
                    ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
 | 
			
		||||
                    if (ds == null) return loc1;
 | 
			
		||||
 | 
			
		||||
                    var fkColumns = new Dictionary<int, Dictionary<string, DbForeignInfo>>();
 | 
			
		||||
                    foreach (object[] row in ds)
 | 
			
		||||
                    {
 | 
			
		||||
                        int object_id, referenced_object_id;
 | 
			
		||||
                        int.TryParse(string.Concat(row[0]), out object_id);
 | 
			
		||||
                        var column = string.Concat(row[1]);
 | 
			
		||||
                        string fk_id = string.Concat(row[2]);
 | 
			
		||||
                        int.TryParse(string.Concat(row[3]), out referenced_object_id);
 | 
			
		||||
                        var is_foreign_key = bool.Parse(string.Concat(row[4]));
 | 
			
		||||
                        var referenced_column = string.Concat(row[5]);
 | 
			
		||||
                        var referenced_db = string.Concat(row[6]);
 | 
			
		||||
                        var referenced_table = string.Concat(row[7]);
 | 
			
		||||
                        DbColumnInfo loc9 = loc3[object_id][column];
 | 
			
		||||
                        DbTableInfo loc10 = null;
 | 
			
		||||
                        DbColumnInfo loc11 = null;
 | 
			
		||||
                        bool isThisSln = referenced_object_id != 0;
 | 
			
		||||
 | 
			
		||||
                        if (isThisSln)
 | 
			
		||||
                        {
 | 
			
		||||
                            loc10 = loc2[referenced_object_id];
 | 
			
		||||
                            loc11 = loc3[referenced_object_id][referenced_column];
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
 | 
			
		||||
                        }
 | 
			
		||||
                        Dictionary<string, DbForeignInfo> loc12 = null;
 | 
			
		||||
                        DbForeignInfo loc13 = null;
 | 
			
		||||
                        if (!fkColumns.TryGetValue(object_id, out loc12))
 | 
			
		||||
                            fkColumns.Add(object_id, loc12 = new Dictionary<string, DbForeignInfo>());
 | 
			
		||||
                        if (!loc12.TryGetValue(fk_id, out loc13))
 | 
			
		||||
                            loc12.Add(fk_id, loc13 = new DbForeignInfo { Table = loc2[object_id], ReferencedTable = loc10 });
 | 
			
		||||
                        loc13.Columns.Add(loc9);
 | 
			
		||||
                        loc13.ReferencedColumns.Add(loc11);
 | 
			
		||||
                    }
 | 
			
		||||
                    foreach (var table_id in fkColumns.Keys)
 | 
			
		||||
                        foreach (var fk in fkColumns[table_id])
 | 
			
		||||
                            loc2[table_id].ForeignsDict.Add(fk.Key, fk.Value);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                foreach (var table_id in loc3.Keys)
 | 
			
		||||
                {
 | 
			
		||||
                    foreach (var loc5 in loc3[table_id].Values)
 | 
			
		||||
                    {
 | 
			
		||||
                        loc2[table_id].Columns.Add(loc5);
 | 
			
		||||
                        if (loc5.IsIdentity) loc2[table_id].Identitys.Add(loc5);
 | 
			
		||||
                        if (loc5.IsPrimary) loc2[table_id].Primarys.Add(loc5);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                foreach (var loc4 in loc2.Values)
 | 
			
		||||
                {
 | 
			
		||||
                    //if (loc4.Primarys.Count == 0 && loc4.UniquesDict.Count > 0)
 | 
			
		||||
                    //{
 | 
			
		||||
                    //    foreach (var loc5 in loc4.UniquesDict.First().Value.Columns)
 | 
			
		||||
                    //    {
 | 
			
		||||
                    //        loc5.Column.IsPrimary = true;
 | 
			
		||||
                    //        loc4.Primarys.Add(loc5.Column);
 | 
			
		||||
                    //    }
 | 
			
		||||
                    //}
 | 
			
		||||
                    loc4.Primarys.Sort((c1, c2) => c1.Name.CompareTo(c2.Name));
 | 
			
		||||
                    loc4.Columns.Sort((c1, c2) =>
 | 
			
		||||
                    {
 | 
			
		||||
                        int compare = c2.IsPrimary.CompareTo(c1.IsPrimary);
 | 
			
		||||
                        if (compare == 0)
 | 
			
		||||
                        {
 | 
			
		||||
                            bool b1 = loc4.ForeignsDict.Values.Where(fk => fk.Columns.Where(c3 => c3.Name == c1.Name).Any()).Any();
 | 
			
		||||
                            bool b2 = loc4.ForeignsDict.Values.Where(fk => fk.Columns.Where(c3 => c3.Name == c2.Name).Any()).Any();
 | 
			
		||||
                            compare = b2.CompareTo(b1);
 | 
			
		||||
                        }
 | 
			
		||||
                        if (compare == 0) compare = c1.Name.CompareTo(c2.Name);
 | 
			
		||||
                        return compare;
 | 
			
		||||
                    });
 | 
			
		||||
                    loc1.Add(loc4);
 | 
			
		||||
                }
 | 
			
		||||
                loc1.Sort((t1, t2) =>
 | 
			
		||||
                {
 | 
			
		||||
                    var ret = t1.Schema.CompareTo(t2.Schema);
 | 
			
		||||
                    if (ret == 0) ret = t1.Name.CompareTo(t2.Name);
 | 
			
		||||
                    return ret;
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                loc2.Clear();
 | 
			
		||||
                loc3.Clear();
 | 
			
		||||
                tables.AddRange(loc1);
 | 
			
		||||
            }
 | 
			
		||||
            return tables;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public List<DbEnumInfo> GetEnumsByDatabase(params string[] database)
 | 
			
		||||
        {
 | 
			
		||||
            return new List<DbEnumInfo>();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,558 @@
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Linq.Expressions;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Text.RegularExpressions;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Custom.SqlServer
 | 
			
		||||
{
 | 
			
		||||
    class CustomSqlServerExpression : CommonExpression
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        public CustomSqlServerExpression(CommonUtils common) : base(common) { }
 | 
			
		||||
 | 
			
		||||
        public override string ExpressionLambdaToSqlOther(Expression exp, ExpTSC tsc)
 | 
			
		||||
        {
 | 
			
		||||
            Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
 | 
			
		||||
            switch (exp.NodeType)
 | 
			
		||||
            {
 | 
			
		||||
                case ExpressionType.ArrayLength:
 | 
			
		||||
                    var arrOper = (exp as UnaryExpression)?.Operand;
 | 
			
		||||
                    if (arrOper.Type == typeof(byte[])) return $"datalength({getExp(arrOper)})";
 | 
			
		||||
                    break;
 | 
			
		||||
                case ExpressionType.Convert:
 | 
			
		||||
                    var operandExp = (exp as UnaryExpression)?.Operand;
 | 
			
		||||
                    var gentype = exp.Type.NullableTypeOrThis();
 | 
			
		||||
                    if (gentype != operandExp.Type.NullableTypeOrThis())
 | 
			
		||||
                    {
 | 
			
		||||
                        switch (gentype.ToString())
 | 
			
		||||
                        {
 | 
			
		||||
                            case "System.Boolean": return $"(cast({getExp(operandExp)} as varchar) not in ('0','false'))";
 | 
			
		||||
                            case "System.Byte": return $"cast({getExp(operandExp)} as tinyint)";
 | 
			
		||||
                            case "System.Char": return $"substring(cast({getExp(operandExp)} as nvarchar),1,1)";
 | 
			
		||||
                            case "System.DateTime": return ExpressionConstDateTime(operandExp) ?? $"cast({getExp(operandExp)} as datetime)";
 | 
			
		||||
                            case "System.Decimal": return $"cast({getExp(operandExp)} as decimal(36,18))";
 | 
			
		||||
                            case "System.Double": return $"cast({getExp(operandExp)} as decimal(32,16))";
 | 
			
		||||
                            case "System.Int16": return $"cast({getExp(operandExp)} as smallint)";
 | 
			
		||||
                            case "System.Int32": return $"cast({getExp(operandExp)} as int)";
 | 
			
		||||
                            case "System.Int64": return $"cast({getExp(operandExp)} as bigint)";
 | 
			
		||||
                            case "System.SByte": return $"cast({getExp(operandExp)} as tinyint)";
 | 
			
		||||
                            case "System.Single": return $"cast({getExp(operandExp)} as decimal(14,7))";
 | 
			
		||||
                            case "System.String":
 | 
			
		||||
                                return gentype == typeof(Guid) ?
 | 
			
		||||
                                  $"cast({getExp(operandExp)} as varchar(36))" :
 | 
			
		||||
                                  $"cast({getExp(operandExp)} as nvarchar{(gentype.IsNumberType() || gentype.IsEnum ? "(100)" : "(max)")})";
 | 
			
		||||
                            case "System.UInt16": return $"cast({getExp(operandExp)} as smallint)";
 | 
			
		||||
                            case "System.UInt32": return $"cast({getExp(operandExp)} as int)";
 | 
			
		||||
                            case "System.UInt64": return $"cast({getExp(operandExp)} as bigint)";
 | 
			
		||||
                            case "System.Guid": return $"cast({getExp(operandExp)} as uniqueidentifier)";
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                case ExpressionType.Call:
 | 
			
		||||
                    var callExp = exp as MethodCallExpression;
 | 
			
		||||
 | 
			
		||||
                    switch (callExp.Method.Name)
 | 
			
		||||
                    {
 | 
			
		||||
                        case "Parse":
 | 
			
		||||
                        case "TryParse":
 | 
			
		||||
                            switch (callExp.Method.DeclaringType.NullableTypeOrThis().ToString())
 | 
			
		||||
                            {
 | 
			
		||||
                                case "System.Boolean": return $"(cast({getExp(callExp.Arguments[0])} as varchar) not in ('0','false'))";
 | 
			
		||||
                                case "System.Byte": return $"cast({getExp(callExp.Arguments[0])} as tinyint)";
 | 
			
		||||
                                case "System.Char": return $"substring(cast({getExp(callExp.Arguments[0])} as nvarchar),1,1)";
 | 
			
		||||
                                case "System.DateTime": return ExpressionConstDateTime(callExp.Arguments[0]) ?? $"cast({getExp(callExp.Arguments[0])} as datetime)";
 | 
			
		||||
                                case "System.Decimal": return $"cast({getExp(callExp.Arguments[0])} as decimal(36,18))";
 | 
			
		||||
                                case "System.Double": return $"cast({getExp(callExp.Arguments[0])} as decimal(32,16))";
 | 
			
		||||
                                case "System.Int16": return $"cast({getExp(callExp.Arguments[0])} as smallint)";
 | 
			
		||||
                                case "System.Int32": return $"cast({getExp(callExp.Arguments[0])} as int)";
 | 
			
		||||
                                case "System.Int64": return $"cast({getExp(callExp.Arguments[0])} as bigint)";
 | 
			
		||||
                                case "System.SByte": return $"cast({getExp(callExp.Arguments[0])} as tinyint)";
 | 
			
		||||
                                case "System.Single": return $"cast({getExp(callExp.Arguments[0])} as decimal(14,7))";
 | 
			
		||||
                                case "System.UInt16": return $"cast({getExp(callExp.Arguments[0])} as smallint)";
 | 
			
		||||
                                case "System.UInt32": return $"cast({getExp(callExp.Arguments[0])} as int)";
 | 
			
		||||
                                case "System.UInt64": return $"cast({getExp(callExp.Arguments[0])} as bigint)";
 | 
			
		||||
                                case "System.Guid": return $"cast({getExp(callExp.Arguments[0])} as uniqueidentifier)";
 | 
			
		||||
                            }
 | 
			
		||||
                            return null;
 | 
			
		||||
                        case "NewGuid":
 | 
			
		||||
                            switch (callExp.Method.DeclaringType.NullableTypeOrThis().ToString())
 | 
			
		||||
                            {
 | 
			
		||||
                                case "System.Guid": return $"newid()";
 | 
			
		||||
                            }
 | 
			
		||||
                            return null;
 | 
			
		||||
                        case "Next":
 | 
			
		||||
                            if (callExp.Object?.Type == typeof(Random)) return "cast(rand()*1000000000 as int)";
 | 
			
		||||
                            return null;
 | 
			
		||||
                        case "NextDouble":
 | 
			
		||||
                            if (callExp.Object?.Type == typeof(Random)) return "rand()";
 | 
			
		||||
                            return null;
 | 
			
		||||
                        case "Random":
 | 
			
		||||
                            if (callExp.Method.DeclaringType.IsNumberType()) return "rand()";
 | 
			
		||||
                            return null;
 | 
			
		||||
                        case "ToString":
 | 
			
		||||
                            var gentype2 = callExp.Object.Type.NullableTypeOrThis();
 | 
			
		||||
                            if (callExp.Object != null) return callExp.Arguments.Count == 0 ? (gentype2 == typeof(Guid) ?
 | 
			
		||||
                                    $"cast({getExp(callExp.Object)} as varchar(36))" :
 | 
			
		||||
                                    $"cast({getExp(callExp.Object)} as nvarchar{(gentype2.IsNumberType() || gentype2.IsEnum ? "(100)" : "(max)")})") : null;
 | 
			
		||||
                            return null;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    var objExp = callExp.Object;
 | 
			
		||||
                    var objType = objExp?.Type;
 | 
			
		||||
                    if (objType?.FullName == "System.Byte[]") return null;
 | 
			
		||||
 | 
			
		||||
                    var argIndex = 0;
 | 
			
		||||
                    if (objType == null && callExp.Method.DeclaringType == typeof(Enumerable))
 | 
			
		||||
                    {
 | 
			
		||||
                        objExp = callExp.Arguments.FirstOrDefault();
 | 
			
		||||
                        objType = objExp?.Type;
 | 
			
		||||
                        argIndex++;
 | 
			
		||||
 | 
			
		||||
                        if (objType == typeof(string))
 | 
			
		||||
                        {
 | 
			
		||||
                            switch (callExp.Method.Name)
 | 
			
		||||
                            {
 | 
			
		||||
                                case "First":
 | 
			
		||||
                                case "FirstOrDefault":
 | 
			
		||||
                                    return $"substring({getExp(callExp.Arguments[0])}, 1, 1)";
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    if (objType == null) objType = callExp.Method.DeclaringType;
 | 
			
		||||
                    if (objType != null || objType.IsArrayOrList())
 | 
			
		||||
                    {
 | 
			
		||||
                        if (argIndex >= callExp.Arguments.Count) break;
 | 
			
		||||
                        tsc.SetMapColumnTmp(null);
 | 
			
		||||
                        var args1 = getExp(callExp.Arguments[argIndex]);
 | 
			
		||||
                        var oldMapType = tsc.SetMapTypeReturnOld(tsc.mapTypeTmp);
 | 
			
		||||
                        var oldDbParams = objExp?.NodeType == ExpressionType.MemberAccess ? tsc.SetDbParamsReturnOld(null) : null; //#900 UseGenerateCommandParameterWithLambda(true) 子查询 bug、以及 #1173 参数化 bug
 | 
			
		||||
                        tsc.isNotSetMapColumnTmp = true;
 | 
			
		||||
                        var left = objExp == null ? null : getExp(objExp);
 | 
			
		||||
                        tsc.isNotSetMapColumnTmp = false;
 | 
			
		||||
                        tsc.SetMapColumnTmp(null).SetMapTypeReturnOld(oldMapType);
 | 
			
		||||
                        if (oldDbParams != null) tsc.SetDbParamsReturnOld(oldDbParams);
 | 
			
		||||
                        switch (callExp.Method.Name)
 | 
			
		||||
                        {
 | 
			
		||||
                            case "Contains":
 | 
			
		||||
                                //判断 in //在各大 Provider AdoProvider 中已约定,500元素分割, 3空格\r\n4空格
 | 
			
		||||
                                return $"(({args1}) in {left.Replace(",   \r\n    \r\n", $") \r\n OR ({args1}) in (")})";
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                case ExpressionType.NewArrayInit:
 | 
			
		||||
                    var arrExp = exp as NewArrayExpression;
 | 
			
		||||
                    var arrSb = new StringBuilder();
 | 
			
		||||
                    arrSb.Append("(");
 | 
			
		||||
                    for (var a = 0; a < arrExp.Expressions.Count; a++)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (a > 0) arrSb.Append(",");
 | 
			
		||||
                        if (a % 500 == 499) arrSb.Append("   \r\n    \r\n"); //500元素分割, 3空格\r\n4空格
 | 
			
		||||
                        arrSb.Append(getExp(arrExp.Expressions[a]));
 | 
			
		||||
                    }
 | 
			
		||||
                    if (arrSb.Length == 1) arrSb.Append("NULL");
 | 
			
		||||
                    return arrSb.Append(")").ToString();
 | 
			
		||||
                case ExpressionType.ListInit:
 | 
			
		||||
                    var listExp = exp as ListInitExpression;
 | 
			
		||||
                    var listSb = new StringBuilder();
 | 
			
		||||
                    listSb.Append("(");
 | 
			
		||||
                    for (var a = 0; a < listExp.Initializers.Count; a++)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (listExp.Initializers[a].Arguments.Any() == false) continue;
 | 
			
		||||
                        if (a > 0) listSb.Append(",");
 | 
			
		||||
                        listSb.Append(getExp(listExp.Initializers[a].Arguments.FirstOrDefault()));
 | 
			
		||||
                    }
 | 
			
		||||
                    if (listSb.Length == 1) listSb.Append("NULL");
 | 
			
		||||
                    return listSb.Append(")").ToString();
 | 
			
		||||
                case ExpressionType.New:
 | 
			
		||||
                    var newExp = exp as NewExpression;
 | 
			
		||||
                    if (typeof(IList).IsAssignableFrom(newExp.Type))
 | 
			
		||||
                    {
 | 
			
		||||
                        if (newExp.Arguments.Count == 0) return "(NULL)";
 | 
			
		||||
                        if (typeof(IEnumerable).IsAssignableFrom(newExp.Arguments[0].Type) == false) return "(NULL)";
 | 
			
		||||
                        return getExp(newExp.Arguments[0]);
 | 
			
		||||
                    }
 | 
			
		||||
                    return null;
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override string ExpressionLambdaToSqlMemberAccessString(MemberExpression exp, ExpTSC tsc)
 | 
			
		||||
        {
 | 
			
		||||
            if (exp.Expression == null)
 | 
			
		||||
            {
 | 
			
		||||
                switch (exp.Member.Name)
 | 
			
		||||
                {
 | 
			
		||||
                    case "Empty": return "''";
 | 
			
		||||
                }
 | 
			
		||||
                return null;
 | 
			
		||||
            }
 | 
			
		||||
            var left = ExpressionLambdaToSql(exp.Expression, tsc);
 | 
			
		||||
            switch (exp.Member.Name)
 | 
			
		||||
            {
 | 
			
		||||
                case "Length": return $"len({left})";
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        public override string ExpressionLambdaToSqlMemberAccessDateTime(MemberExpression exp, ExpTSC tsc)
 | 
			
		||||
        {
 | 
			
		||||
            if (exp.Expression == null)
 | 
			
		||||
            {
 | 
			
		||||
                switch (exp.Member.Name)
 | 
			
		||||
                {
 | 
			
		||||
                    case "Now": return _common.Now;
 | 
			
		||||
                    case "UtcNow": return _common.NowUtc;
 | 
			
		||||
                    case "Today": return "convert(char(10),getdate(),120)";
 | 
			
		||||
                    case "MinValue": return "'1753/1/1 0:00:00'";
 | 
			
		||||
                    case "MaxValue": return "'9999/12/31 23:59:59'";
 | 
			
		||||
                }
 | 
			
		||||
                return null;
 | 
			
		||||
            }
 | 
			
		||||
            var left = ExpressionLambdaToSql(exp.Expression, tsc);
 | 
			
		||||
            switch (exp.Member.Name)
 | 
			
		||||
            {
 | 
			
		||||
                case "Date": return $"convert(char(10),{left},120)";
 | 
			
		||||
                case "TimeOfDay": return $"datediff(second, convert(char(10),{left},120), {left})";
 | 
			
		||||
                case "DayOfWeek": return $"(datepart(weekday, {left})-1)";
 | 
			
		||||
                case "Day": return $"datepart(day, {left})";
 | 
			
		||||
                case "DayOfYear": return $"datepart(dayofyear, {left})";
 | 
			
		||||
                case "Month": return $"datepart(month, {left})";
 | 
			
		||||
                case "Year": return $"datepart(year, {left})";
 | 
			
		||||
                case "Hour": return $"datepart(hour, {left})";
 | 
			
		||||
                case "Minute": return $"datepart(minute, {left})";
 | 
			
		||||
                case "Second": return $"datepart(second, {left})";
 | 
			
		||||
                case "Millisecond": return $"(datepart(millisecond, {left})/1000)";
 | 
			
		||||
                case "Ticks": return $"(cast(datediff(second, '1970-1-1', {left}) as bigint)*10000000+621355968000000000)";
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        public override string ExpressionLambdaToSqlMemberAccessTimeSpan(MemberExpression exp, ExpTSC tsc)
 | 
			
		||||
        {
 | 
			
		||||
            if (exp.Expression == null)
 | 
			
		||||
            {
 | 
			
		||||
                switch (exp.Member.Name)
 | 
			
		||||
                {
 | 
			
		||||
                    case "Zero": return "0";
 | 
			
		||||
                    case "MinValue": return "-922337203685477580"; //微秒 Ticks / 10
 | 
			
		||||
                    case "MaxValue": return "922337203685477580";
 | 
			
		||||
                }
 | 
			
		||||
                return null;
 | 
			
		||||
            }
 | 
			
		||||
            var left = ExpressionLambdaToSql(exp.Expression, tsc);
 | 
			
		||||
            switch (exp.Member.Name)
 | 
			
		||||
            {
 | 
			
		||||
                case "Days": return $"floor(({left})/{60 * 60 * 24})";
 | 
			
		||||
                case "Hours": return $"floor(({left})/{60 * 60}%24)";
 | 
			
		||||
                case "Milliseconds": return $"(cast({left} as bigint)*1000)";
 | 
			
		||||
                case "Minutes": return $"floor(({left})/60%60)";
 | 
			
		||||
                case "Seconds": return $"(({left})%60)";
 | 
			
		||||
                case "Ticks": return $"(cast({left} as bigint)*10000000)";
 | 
			
		||||
                case "TotalDays": return $"(({left})/{60 * 60 * 24})";
 | 
			
		||||
                case "TotalHours": return $"(({left})/{60 * 60})";
 | 
			
		||||
                case "TotalMilliseconds": return $"(cast({left} as bigint)*1000)";
 | 
			
		||||
                case "TotalMinutes": return $"(({left})/60)";
 | 
			
		||||
                case "TotalSeconds": return $"({left})";
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override string ExpressionLambdaToSqlCallString(MethodCallExpression exp, ExpTSC tsc)
 | 
			
		||||
        {
 | 
			
		||||
            Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
 | 
			
		||||
            if (exp.Object == null)
 | 
			
		||||
            {
 | 
			
		||||
                switch (exp.Method.Name)
 | 
			
		||||
                {
 | 
			
		||||
                    case "IsNullOrEmpty":
 | 
			
		||||
                        var arg1 = getExp(exp.Arguments[0]);
 | 
			
		||||
                        return $"({arg1} is null or {arg1} = '')";
 | 
			
		||||
                    case "IsNullOrWhiteSpace":
 | 
			
		||||
                        var arg2 = getExp(exp.Arguments[0]);
 | 
			
		||||
                        return $"({arg2} is null or {arg2} = '' or ltrim({arg2}) = '')";
 | 
			
		||||
                    case "Concat":
 | 
			
		||||
                        return _common.StringConcat(exp.Arguments.Select(a => getExp(a)).ToArray(), exp.Arguments.Select(a => a.Type).ToArray());
 | 
			
		||||
                    case "Format":
 | 
			
		||||
                        if (exp.Arguments[0].NodeType != ExpressionType.Constant) throw new Exception(CoreStrings.Not_Implemented_Expression_ParameterUseConstant(exp,exp.Arguments[0]));
 | 
			
		||||
                        var expArgs0 = ExpressionLambdaToSql(exp.Arguments[0], tsc);
 | 
			
		||||
                        if (exp.Arguments.Count == 1) return expArgs0;
 | 
			
		||||
                        var nchar = expArgs0.StartsWith("N'") ? "N" : "";
 | 
			
		||||
                        var expArgsHack = exp.Arguments.Count == 2 && exp.Arguments[1].NodeType == ExpressionType.NewArrayInit ?
 | 
			
		||||
                            (exp.Arguments[1] as NewArrayExpression).Expressions : exp.Arguments.Where((a, z) => z > 0);
 | 
			
		||||
                        //3个 {} 时,Arguments 解析出来是分开的
 | 
			
		||||
                        //4个 {} 时,Arguments[1] 只能解析这个出来,然后里面是 NewArray []
 | 
			
		||||
                        var expArgs = expArgsHack.Select(a =>
 | 
			
		||||
                        {
 | 
			
		||||
                            var atype = (a as UnaryExpression)?.Operand.Type.NullableTypeOrThis() ?? a.Type.NullableTypeOrThis();
 | 
			
		||||
                            if (atype == typeof(string)) return $"'+{_common.IsNull(ExpressionLambdaToSql(a, tsc), "''")}+{nchar}'";
 | 
			
		||||
                            if (atype == typeof(Guid)) return $"'+{_common.IsNull($"cast({ExpressionLambdaToSql(a, tsc)} as char(36))", "''")}+{nchar}'";
 | 
			
		||||
                            if (atype.IsNumberType()) return $"'+{_common.IsNull($"cast({ExpressionLambdaToSql(a, tsc)} as varchar)", "''")}+{nchar}'";
 | 
			
		||||
                            return $"'+{_common.IsNull($"cast({ExpressionLambdaToSql(a, tsc)} as nvarchar(max))", "''")}+{nchar}'";
 | 
			
		||||
                        }).ToArray();
 | 
			
		||||
                        return string.Format(expArgs0, expArgs);
 | 
			
		||||
                    case "Join":
 | 
			
		||||
                        if (exp.IsStringJoin(out var tolistObjectExp, out var toListMethod, out var toListArgs1))
 | 
			
		||||
                        {
 | 
			
		||||
                            var newToListArgs0 = Expression.Call(tolistObjectExp, toListMethod,
 | 
			
		||||
                                Expression.Lambda(
 | 
			
		||||
                                    Expression.Call(
 | 
			
		||||
                                        typeof(string).GetMethod("Concat", new[] { typeof(object), typeof(object) }),
 | 
			
		||||
                                        Expression.Convert(exp.Arguments[0], typeof(object)),
 | 
			
		||||
                                        Expression.Convert(toListArgs1.Body, typeof(object))),
 | 
			
		||||
                                    toListArgs1.Parameters));
 | 
			
		||||
                            var newToListSql = getExp(newToListArgs0);
 | 
			
		||||
                            if (string.IsNullOrEmpty(newToListSql) == false && newToListSql.StartsWith("(") && newToListSql.EndsWith(")"))
 | 
			
		||||
                            {
 | 
			
		||||
                                newToListSql = $"stuff({newToListSql.Substring(0, newToListSql.Length - 1)} FOR XML PATH('')),1,len({getExp(exp.Arguments[0])}),'')";
 | 
			
		||||
                                return newToListSql;
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                var left = getExp(exp.Object);
 | 
			
		||||
                switch (exp.Method.Name)
 | 
			
		||||
                {
 | 
			
		||||
                    case "StartsWith":
 | 
			
		||||
                    case "EndsWith":
 | 
			
		||||
                    case "Contains":
 | 
			
		||||
                        var args0Value = getExp(exp.Arguments[0]);
 | 
			
		||||
                        if (args0Value == "NULL") return $"({left}) IS NULL";
 | 
			
		||||
                        if (args0Value.Contains("%"))
 | 
			
		||||
                        {
 | 
			
		||||
                            if (exp.Method.Name == "StartsWith") return $"charindex({args0Value}, {left}) = 1";
 | 
			
		||||
                            if (exp.Method.Name == "EndsWith") return $"charindex({args0Value}, {left}) = len({args0Value})";
 | 
			
		||||
                            return $"charindex({args0Value}, {left}) > 0";
 | 
			
		||||
                        }
 | 
			
		||||
                        if (exp.Method.Name == "StartsWith") return $"({left}) LIKE {(args0Value.EndsWith("'") ? args0Value.Insert(args0Value.Length - 1, "%") : $"(cast({args0Value} as nvarchar(max))+'%')")}";
 | 
			
		||||
                        if (exp.Method.Name == "EndsWith") return $"({left}) LIKE {(args0Value.StartsWith("'") ? args0Value.Insert(1, "%") : $"('%'+cast({args0Value} as nvarchar(max)))")}";
 | 
			
		||||
                        if (args0Value.StartsWith("'") && args0Value.EndsWith("'")) return $"({left}) LIKE {args0Value.Insert(1, "%").Insert(args0Value.Length, "%")}";
 | 
			
		||||
                        return $"({left}) LIKE ('%'+cast({args0Value} as nvarchar(max))+'%')";
 | 
			
		||||
                    case "ToLower": return $"lower({left})";
 | 
			
		||||
                    case "ToUpper": return $"upper({left})";
 | 
			
		||||
                    case "Substring":
 | 
			
		||||
                        var substrArgs1 = getExp(exp.Arguments[0]);
 | 
			
		||||
                        if (long.TryParse(substrArgs1, out var testtrylng1)) substrArgs1 = (testtrylng1 + 1).ToString();
 | 
			
		||||
                        else substrArgs1 += "+1";
 | 
			
		||||
                        if (exp.Arguments.Count == 1) return $"left({left}, {substrArgs1})";
 | 
			
		||||
                        return $"substring({left}, {substrArgs1}, {getExp(exp.Arguments[1])})";
 | 
			
		||||
                    case "IndexOf":
 | 
			
		||||
                        var indexOfFindStr = getExp(exp.Arguments[0]);
 | 
			
		||||
                        if (exp.Arguments.Count > 1 && exp.Arguments[1].Type.FullName == "System.Int32")
 | 
			
		||||
                        {
 | 
			
		||||
                            var locateArgs1 = getExp(exp.Arguments[1]);
 | 
			
		||||
                            if (long.TryParse(locateArgs1, out var testtrylng2)) locateArgs1 = (testtrylng2 + 1).ToString();
 | 
			
		||||
                            else locateArgs1 += "+1";
 | 
			
		||||
                            return $"(charindex({indexOfFindStr}, {left}, {locateArgs1})-1)";
 | 
			
		||||
                        }
 | 
			
		||||
                        return $"(charindex({indexOfFindStr}, {left})-1)";
 | 
			
		||||
                    case "PadLeft":
 | 
			
		||||
                        if (exp.Arguments.Count == 1) return $"lpad({left}, {getExp(exp.Arguments[0])})";
 | 
			
		||||
                        return $"lpad({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
 | 
			
		||||
                    case "PadRight":
 | 
			
		||||
                        if (exp.Arguments.Count == 1) return $"rpad({left}, {getExp(exp.Arguments[0])})";
 | 
			
		||||
                        return $"rpad({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
 | 
			
		||||
                    case "Trim": return $"ltrim(rtrim({left}))";
 | 
			
		||||
                    case "TrimStart": return $"ltrim({left})";
 | 
			
		||||
                    case "TrimEnd": return $"rtrim({left})";
 | 
			
		||||
                    case "Replace": return $"replace({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
 | 
			
		||||
                    case "CompareTo": return $"({left} - {getExp(exp.Arguments[0])})";
 | 
			
		||||
                    case "Equals": return $"({left} = {getExp(exp.Arguments[0])})";
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        public override string ExpressionLambdaToSqlCallMath(MethodCallExpression exp, ExpTSC tsc)
 | 
			
		||||
        {
 | 
			
		||||
            Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
 | 
			
		||||
            switch (exp.Method.Name)
 | 
			
		||||
            {
 | 
			
		||||
                case "Abs": return $"abs({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Sign": return $"sign({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Floor": return $"floor({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Ceiling": return $"ceiling({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Round":
 | 
			
		||||
                    if (exp.Arguments.Count > 1 && exp.Arguments[1].Type.FullName == "System.Int32") return $"round({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
 | 
			
		||||
                    return $"round({getExp(exp.Arguments[0])}, 0)";
 | 
			
		||||
                case "Exp": return $"exp({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Log": return $"log({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Log10": return $"log10({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Pow": return $"power({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
 | 
			
		||||
                case "Sqrt": return $"sqrt({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Cos": return $"cos({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Sin": return $"sin({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Tan": return $"tan({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Acos": return $"acos({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Asin": return $"asin({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Atan": return $"atan({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Atan2": return $"atan2({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
 | 
			
		||||
                case "Truncate": return $"floor({getExp(exp.Arguments[0])})";
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        public override string ExpressionLambdaToSqlCallDateTime(MethodCallExpression exp, ExpTSC tsc)
 | 
			
		||||
        {
 | 
			
		||||
            Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
 | 
			
		||||
            if (exp.Object == null)
 | 
			
		||||
            {
 | 
			
		||||
                switch (exp.Method.Name)
 | 
			
		||||
                {
 | 
			
		||||
                    case "Compare": return $"({getExp(exp.Arguments[0])} - ({getExp(exp.Arguments[1])}))";
 | 
			
		||||
                    case "DaysInMonth": return $"datepart(day, dateadd(day, -1, dateadd(month, 1, cast({getExp(exp.Arguments[0])} as varchar(100)) + '-' + cast({getExp(exp.Arguments[1])} as varchar(100)) + '-1')))";
 | 
			
		||||
                    case "Equals": return $"({getExp(exp.Arguments[0])} = {getExp(exp.Arguments[1])})";
 | 
			
		||||
 | 
			
		||||
                    case "IsLeapYear":
 | 
			
		||||
                        var isLeapYearArgs1 = getExp(exp.Arguments[0]);
 | 
			
		||||
                        return $"(({isLeapYearArgs1})%4=0 AND ({isLeapYearArgs1})%100<>0 OR ({isLeapYearArgs1})%400=0)";
 | 
			
		||||
 | 
			
		||||
                    case "Parse": return ExpressionConstDateTime(exp.Arguments[0]) ?? $"cast({getExp(exp.Arguments[0])} as datetime)";
 | 
			
		||||
                    case "ParseExact":
 | 
			
		||||
                    case "TryParse":
 | 
			
		||||
                    case "TryParseExact": return ExpressionConstDateTime(exp.Arguments[0]) ?? $"cast({getExp(exp.Arguments[0])} as datetime)";
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                var left = getExp(exp.Object);
 | 
			
		||||
                var args1 = exp.Arguments.Count == 0 ? null : getExp(exp.Arguments[0]);
 | 
			
		||||
                switch (exp.Method.Name)
 | 
			
		||||
                {
 | 
			
		||||
                    case "Add": return $"dateadd(second, {args1}, {left})";
 | 
			
		||||
                    case "AddDays": return $"dateadd(day, {args1}, {left})";
 | 
			
		||||
                    case "AddHours": return $"dateadd(hour, {args1}, {left})";
 | 
			
		||||
                    case "AddMilliseconds": return $"dateadd(second, ({args1})/1000, {left})";
 | 
			
		||||
                    case "AddMinutes": return $"dateadd(minute, {args1}, {left})";
 | 
			
		||||
                    case "AddMonths": return $"dateadd(month, {args1}, {left})";
 | 
			
		||||
                    case "AddSeconds": return $"dateadd(second, {args1}, {left})";
 | 
			
		||||
                    case "AddTicks": return $"dateadd(second, ({args1})/10000000, {left})";
 | 
			
		||||
                    case "AddYears": return $"dateadd(year, {args1}, {left})";
 | 
			
		||||
                    case "Subtract":
 | 
			
		||||
                        switch ((exp.Arguments[0].Type.IsNullableType() ? exp.Arguments[0].Type.GetGenericArguments().FirstOrDefault() : exp.Arguments[0].Type).FullName)
 | 
			
		||||
                        {
 | 
			
		||||
                            case "System.DateTime": return $"datediff(second, {args1}, {left})";
 | 
			
		||||
                            case "System.TimeSpan": return $"dateadd(second, ({args1})*-1, {left})";
 | 
			
		||||
                        }
 | 
			
		||||
                        break;
 | 
			
		||||
                    case "Equals": return $"({left} = {args1})";
 | 
			
		||||
                    case "CompareTo": return $"datediff(second,{args1},{left})";
 | 
			
		||||
                    case "ToString":
 | 
			
		||||
                        if (left.EndsWith(" as datetime)") == false) left = $"cast({left} as datetime)";
 | 
			
		||||
                        if (exp.Arguments.Count == 0) return $"convert(varchar, {left}, 121)";
 | 
			
		||||
                        switch (args1.TrimStart('N'))
 | 
			
		||||
                        {
 | 
			
		||||
                            case "'yyyy-MM-dd HH:mm:ss'": return $"convert(char(19), {left}, 120)";
 | 
			
		||||
                            case "'yyyy-MM-dd HH:mm'": return $"substring(convert(char(19), {left}, 120), 1, 16)";
 | 
			
		||||
                            case "'yyyy-MM-dd HH'": return $"substring(convert(char(19), {left}, 120), 1, 13)";
 | 
			
		||||
                            case "'yyyy-MM-dd'": return $"convert(char(10), {left}, 23)";
 | 
			
		||||
                            case "'yyyy-MM'": return $"substring(convert(char(10), {left}, 23), 1, 7)";
 | 
			
		||||
                            case "'yyyyMMdd'": return $"convert(char(8), {left}, 112)";
 | 
			
		||||
                            case "'yyyyMM'": return $"substring(convert(char(8), {left}, 112), 1, 6)";
 | 
			
		||||
                            case "'yyyy'": return $"substring(convert(char(8), {left}, 112), 1, 4)";
 | 
			
		||||
                            case "'HH:mm:ss'": return $"convert(char(8), {left}, 24)";
 | 
			
		||||
                        }
 | 
			
		||||
                        var isMatched = false;
 | 
			
		||||
                        var nchar = args1.StartsWith("N'") ? "N" : "";
 | 
			
		||||
                        args1 = Regex.Replace(args1, "(yyyy|yy|MM|M|dd|d|HH|H|hh|h|mm|m|ss|s|tt|t)", m =>
 | 
			
		||||
                        {
 | 
			
		||||
                            isMatched = true;
 | 
			
		||||
                            switch (m.Groups[1].Value)
 | 
			
		||||
                            {
 | 
			
		||||
                                case "yyyy": return $"' + substring(convert(char(8), {left}, 112), 1, 4) + {nchar}'";
 | 
			
		||||
                                case "yy": return $"' + substring(convert(char(6), {left}, 12), 1, 2) + {nchar}'";
 | 
			
		||||
                                case "MM": return $"' + substring(convert(char(6), {left}, 12), 3, 2) + {nchar}'";
 | 
			
		||||
                                case "M": return $"' + case when substring(convert(char(6), {left}, 12), 3, 1) = '0' then substring(convert(char(6), {left}, 12), 4, 1) else substring(convert(char(6), {left}, 12), 3, 2) end + {nchar}'";
 | 
			
		||||
                                case "dd": return $"' + substring(convert(char(6), {left}, 12), 5, 2) + {nchar}'";
 | 
			
		||||
                                case "d": return $"' + case when substring(convert(char(6), {left}, 12), 5, 1) = '0' then substring(convert(char(6), {left}, 12), 6, 1) else substring(convert(char(6), {left}, 12), 5, 2) end + {nchar}'";
 | 
			
		||||
                                case "HH": return $"' + substring(convert(char(8), {left}, 24), 1, 2) + {nchar}'";
 | 
			
		||||
                                case "H": return $"' + case when substring(convert(char(8), {left}, 24), 1, 1) = '0' then substring(convert(char(8), {left}, 24), 2, 1) else substring(convert(char(8), {left}, 24), 1, 2) end + {nchar}'";
 | 
			
		||||
                                case "hh":
 | 
			
		||||
                                    return $"' + case cast(case when substring(convert(char(8), {left}, 24), 1, 1) = '0' then substring(convert(char(8), {left}, 24), 2, 1) else substring(convert(char(8), {left}, 24), 1, 2) end as int) % 12" +
 | 
			
		||||
                             $"when 0 then '12' when 1 then '01' when 2 then '02' when 3 then '03' when 4 then '04' when 5 then '05' when 6 then '06' when 7 then '07' when 8 then '08' when 9 then '09' when 10 then '10' when 11 then '11' end + {nchar}'";
 | 
			
		||||
                                case "h":
 | 
			
		||||
                                    return $"' + case cast(case when substring(convert(char(8), {left}, 24), 1, 1) = '0' then substring(convert(char(8), {left}, 24), 2, 1) else substring(convert(char(8), {left}, 24), 1, 2) end as int) % 12" +
 | 
			
		||||
                              $"when 0 then '12' when 1 then '1' when 2 then '2' when 3 then '3' when 4 then '4' when 5 then '5' when 6 then '6' when 7 then '7' when 8 then '8' when 9 then '9' when 10 then '10' when 11 then '11' end + {nchar}'";
 | 
			
		||||
                                case "mm": return $"' + substring(convert(char(8), {left}, 24), 4, 2) + {nchar}'";
 | 
			
		||||
                                case "m": return $"' + case when substring(convert(char(8), {left}, 24), 4, 1) = '0' then substring(convert(char(8), {left}, 24), 5, 1) else substring(convert(char(8), {left}, 24), 4, 2) end + {nchar}'";
 | 
			
		||||
                                case "ss": return $"' + substring(convert(char(8), {left}, 24), 7, 2) + {nchar}'";
 | 
			
		||||
                                case "s": return $"' + case when substring(convert(char(8), {left}, 24), 7, 1) = '0' then substring(convert(char(8), {left}, 24), 8, 1) else substring(convert(char(8), {left}, 24), 7, 2) end + {nchar}'";
 | 
			
		||||
                                case "tt": return $"' + case when cast(case when substring(convert(char(8), {left}, 24), 1, 1) = '0' then substring(convert(char(8), {left}, 24), 2, 1) else substring(convert(char(8), {left}, 24), 1, 2) end as int) >= 12 then 'PM' else 'AM' end + {nchar}'";
 | 
			
		||||
                                case "t": return $"' + case when cast(case when substring(convert(char(8), {left}, 24), 1, 1) = '0' then substring(convert(char(8), {left}, 24), 2, 1) else substring(convert(char(8), {left}, 24), 1, 2) end as int) >= 12 then 'P' else 'A' end + {nchar}'";
 | 
			
		||||
                            }
 | 
			
		||||
                            return m.Groups[0].Value;
 | 
			
		||||
                        });
 | 
			
		||||
                        return isMatched == false ? args1 : $"({args1})";
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        public override string ExpressionLambdaToSqlCallTimeSpan(MethodCallExpression exp, ExpTSC tsc)
 | 
			
		||||
        {
 | 
			
		||||
            Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
 | 
			
		||||
            if (exp.Object == null)
 | 
			
		||||
            {
 | 
			
		||||
                switch (exp.Method.Name)
 | 
			
		||||
                {
 | 
			
		||||
                    case "Compare": return $"({getExp(exp.Arguments[0])}-({getExp(exp.Arguments[1])}))";
 | 
			
		||||
                    case "Equals": return $"({getExp(exp.Arguments[0])} = {getExp(exp.Arguments[1])})";
 | 
			
		||||
                    case "FromDays": return $"(({getExp(exp.Arguments[0])})*{60 * 60 * 24})";
 | 
			
		||||
                    case "FromHours": return $"(({getExp(exp.Arguments[0])})*{60 * 60})";
 | 
			
		||||
                    case "FromMilliseconds": return $"(({getExp(exp.Arguments[0])})/1000)";
 | 
			
		||||
                    case "FromMinutes": return $"(({getExp(exp.Arguments[0])})*60)";
 | 
			
		||||
                    case "FromSeconds": return $"({getExp(exp.Arguments[0])})";
 | 
			
		||||
                    case "FromTicks": return $"(({getExp(exp.Arguments[0])})/10000000)";
 | 
			
		||||
                    case "Parse": return $"cast({getExp(exp.Arguments[0])} as bigint)";
 | 
			
		||||
                    case "ParseExact":
 | 
			
		||||
                    case "TryParse":
 | 
			
		||||
                    case "TryParseExact": return $"cast({getExp(exp.Arguments[0])} as bigint)";
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                var left = getExp(exp.Object);
 | 
			
		||||
                var args1 = exp.Arguments.Count == 0 ? null : getExp(exp.Arguments[0]);
 | 
			
		||||
                switch (exp.Method.Name)
 | 
			
		||||
                {
 | 
			
		||||
                    case "Add": return $"({left}+{args1})";
 | 
			
		||||
                    case "Subtract": return $"({left}-({args1}))";
 | 
			
		||||
                    case "Equals": return $"({left} = {args1})";
 | 
			
		||||
                    case "CompareTo": return $"({left}-({args1}))";
 | 
			
		||||
                    case "ToString": return $"cast({left} as varchar(100))";
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        public override string ExpressionLambdaToSqlCallConvert(MethodCallExpression exp, ExpTSC tsc)
 | 
			
		||||
        {
 | 
			
		||||
            Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
 | 
			
		||||
            if (exp.Object == null)
 | 
			
		||||
            {
 | 
			
		||||
                switch (exp.Method.Name)
 | 
			
		||||
                {
 | 
			
		||||
                    case "ToBoolean": return $"(cast({getExp(exp.Arguments[0])} as varchar) not in ('0','false'))";
 | 
			
		||||
                    case "ToByte": return $"cast({getExp(exp.Arguments[0])} as tinyint)";
 | 
			
		||||
                    case "ToChar": return $"substring(cast({getExp(exp.Arguments[0])} as nvarchar),1,1)";
 | 
			
		||||
                    case "ToDateTime": return ExpressionConstDateTime(exp.Arguments[0]) ?? $"cast({getExp(exp.Arguments[0])} as datetime)";
 | 
			
		||||
                    case "ToDecimal": return $"cast({getExp(exp.Arguments[0])} as decimal(36,18))";
 | 
			
		||||
                    case "ToDouble": return $"cast({getExp(exp.Arguments[0])} as decimal(32,16))";
 | 
			
		||||
                    case "ToInt16": return $"cast({getExp(exp.Arguments[0])} as smallint)";
 | 
			
		||||
                    case "ToInt32": return $"cast({getExp(exp.Arguments[0])} as int)";
 | 
			
		||||
                    case "ToInt64": return $"cast({getExp(exp.Arguments[0])} as bigint)";
 | 
			
		||||
                    case "ToSByte": return $"cast({getExp(exp.Arguments[0])} as tinyint)";
 | 
			
		||||
                    case "ToSingle": return $"cast({getExp(exp.Arguments[0])} as decimal(14,7))";
 | 
			
		||||
                    case "ToString":
 | 
			
		||||
                        var gentype = exp.Arguments[0].Type.NullableTypeOrThis();
 | 
			
		||||
                        return gentype == typeof(Guid) ?
 | 
			
		||||
                            $"cast({getExp(exp.Arguments[0])} as varchar(36))" :
 | 
			
		||||
                            $"cast({getExp(exp.Arguments[0])} as nvarchar{(gentype.IsNumberType() || gentype.IsEnum ? "(100)" : "(max)")})";
 | 
			
		||||
                    case "ToUInt16": return $"cast({getExp(exp.Arguments[0])} as smallint)";
 | 
			
		||||
                    case "ToUInt32": return $"cast({getExp(exp.Arguments[0])} as int)";
 | 
			
		||||
                    case "ToUInt64": return $"cast({getExp(exp.Arguments[0])} as bigint)";
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,57 @@
 | 
			
		||||
using FreeSql.Internal.CommonProvider;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Data.Common;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Custom.SqlServer
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    public class CustomSqlServerProvider<TMark> : BaseDbProvider, IFreeSql<TMark>
 | 
			
		||||
    {
 | 
			
		||||
        public override ISelect<T1> CreateSelectProvider<T1>(object dywhere) => new CustomSqlServerSelect<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
 | 
			
		||||
        public override IInsert<T1> CreateInsertProvider<T1>() => new CustomSqlServerInsert<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression);
 | 
			
		||||
        public override IUpdate<T1> CreateUpdateProvider<T1>(object dywhere) => new CustomSqlServerUpdate<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
 | 
			
		||||
        public override IDelete<T1> CreateDeleteProvider<T1>(object dywhere) => new CustomSqlServerDelete<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
 | 
			
		||||
        public override IInsertOrUpdate<T1> CreateInsertOrUpdateProvider<T1>() => new CustomSqlServerInsertOrUpdate<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression);
 | 
			
		||||
 | 
			
		||||
        public CustomSqlServerProvider(string masterConnectionString, string[] slaveConnectionString, Func<DbConnection> connectionFactory = null)
 | 
			
		||||
        {
 | 
			
		||||
            this.InternalCommonUtils = new CustomSqlServerUtils(this);
 | 
			
		||||
            this.InternalCommonExpression = new CustomSqlServerExpression(this.InternalCommonUtils);
 | 
			
		||||
 | 
			
		||||
            this.Ado = new CustomSqlServerAdo(this.InternalCommonUtils, masterConnectionString, slaveConnectionString, connectionFactory);
 | 
			
		||||
            this.Aop = new AopProvider();
 | 
			
		||||
 | 
			
		||||
            this.DbFirst = new CustomSqlServerDbFirst(this, this.InternalCommonUtils, this.InternalCommonExpression);
 | 
			
		||||
            this.CodeFirst = new CustomSqlServerCodeFirst(this, this.InternalCommonUtils, this.InternalCommonExpression);
 | 
			
		||||
 | 
			
		||||
            if (this.Ado.MasterPool != null)
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    using (var conn = this.Ado.MasterPool.Get())
 | 
			
		||||
                    {
 | 
			
		||||
                        (this.InternalCommonUtils as CustomSqlServerUtils).ServerVersion = int.Parse(conn.Value.ServerVersion.Split('.')[0]);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                catch
 | 
			
		||||
                {
 | 
			
		||||
                }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ~CustomSqlServerProvider() => this.Dispose();
 | 
			
		||||
        int _disposeCounter;
 | 
			
		||||
        public override void Dispose()
 | 
			
		||||
        {
 | 
			
		||||
            if (Interlocked.Increment(ref _disposeCounter) != 1) return;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                (this.Ado as AdoProvider)?.Dispose();
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
                FreeSqlCustomAdapterGlobalExtensions._dicCustomAdater.TryRemove(Ado.Identifier, out var tryada);
 | 
			
		||||
                FreeSqlCustomAdapterGlobalExtensions._dicDbProviderFactory.TryRemove(Ado.Identifier, out var trydbpf);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,109 @@
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using FreeSql.Internal.CommonProvider;
 | 
			
		||||
using FreeSql.Internal.Model;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Data;
 | 
			
		||||
using System.Data.Common;
 | 
			
		||||
using System.Globalization;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Custom.SqlServer
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    public class CustomSqlServerUtils : CommonUtils
 | 
			
		||||
    {
 | 
			
		||||
        DbProviderFactory Factory => FreeSqlCustomAdapterGlobalExtensions.GetDbProviderFactory(_orm);
 | 
			
		||||
 | 
			
		||||
        public CustomSqlServerUtils(IFreeSql orm) : base(orm)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool IsSelectRowNumber => ServerVersion <= 10;
 | 
			
		||||
        public bool IsSqlServer2005 => ServerVersion == 9;
 | 
			
		||||
        public int ServerVersion = 0;
 | 
			
		||||
 | 
			
		||||
        public override DbParameter AppendParamter(List<DbParameter> _params, string parameterName, ColumnInfo col, Type type, object value)
 | 
			
		||||
        {
 | 
			
		||||
            if (string.IsNullOrEmpty(parameterName)) parameterName = $"p_{_params?.Count}";
 | 
			
		||||
            if (value?.Equals(DateTime.MinValue) == true) value = new DateTime(1970, 1, 1);
 | 
			
		||||
            var ret = Factory.CreateParameter();
 | 
			
		||||
            ret.ParameterName = QuoteParamterName(parameterName);
 | 
			
		||||
            ret.Value = value;
 | 
			
		||||
            var tp = _orm.CodeFirst.GetDbInfo(type)?.type;
 | 
			
		||||
            if (tp != null) ret.DbType = (DbType)tp.Value;
 | 
			
		||||
            _params?.Add(ret);
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override DbParameter[] GetDbParamtersByObject(string sql, object obj) =>
 | 
			
		||||
            Utils.GetDbParamtersByObject<DbParameter>(sql, obj, null, (name, type, value) =>
 | 
			
		||||
            {
 | 
			
		||||
                if (value?.Equals(DateTime.MinValue) == true) value = new DateTime(1970, 1, 1);
 | 
			
		||||
                var ret = Factory.CreateParameter();
 | 
			
		||||
                ret.ParameterName = $"@{name}";
 | 
			
		||||
                ret.Value = value;
 | 
			
		||||
                var tp = _orm.CodeFirst.GetDbInfo(type)?.type;
 | 
			
		||||
                if (tp != null) ret.DbType = (DbType)tp.Value;
 | 
			
		||||
                return ret;
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        public override string FormatSql(string sql, params object[] args) => sql?.FormatCustomSqlServer(args);
 | 
			
		||||
        public override string QuoteSqlName(params string[] name)
 | 
			
		||||
        {
 | 
			
		||||
            if (name.Length == 1)
 | 
			
		||||
            {
 | 
			
		||||
                var nametrim = name[0].Trim();
 | 
			
		||||
                if (nametrim.StartsWith("(") && nametrim.EndsWith(")"))
 | 
			
		||||
                    return nametrim; //原生SQL
 | 
			
		||||
                if (nametrim.StartsWith("[") && nametrim.EndsWith("]"))
 | 
			
		||||
                    return nametrim;
 | 
			
		||||
                return $"[{nametrim.Replace(".", "].[")}]";
 | 
			
		||||
            }
 | 
			
		||||
            return $"[{string.Join("].[", name)}]";
 | 
			
		||||
        }
 | 
			
		||||
        public override string TrimQuoteSqlName(string name)
 | 
			
		||||
        {
 | 
			
		||||
            var nametrim = name.Trim();
 | 
			
		||||
            if (nametrim.StartsWith("(") && nametrim.EndsWith(")"))
 | 
			
		||||
                return nametrim; //原生SQL
 | 
			
		||||
            return $"{nametrim.TrimStart('[').TrimEnd(']').Replace("].[", ".").Replace(".[", ".")}";
 | 
			
		||||
        }
 | 
			
		||||
        public override string[] SplitTableName(string name) => GetSplitTableNames(name, '[', ']', 3);
 | 
			
		||||
        public override string QuoteParamterName(string name) => $"@{name}";
 | 
			
		||||
        public override string IsNull(string sql, object value) => $"isnull({sql}, {value})";
 | 
			
		||||
        public override string StringConcat(string[] objs, Type[] types)
 | 
			
		||||
        {
 | 
			
		||||
            var sb = new StringBuilder();
 | 
			
		||||
            var news = new string[objs.Length];
 | 
			
		||||
            for (var a = 0; a < objs.Length; a++)
 | 
			
		||||
            {
 | 
			
		||||
                if (types[a] == typeof(string)) news[a] = objs[a];
 | 
			
		||||
                else if (types[a].NullableTypeOrThis() == typeof(Guid)) news[a] = $"cast({objs[a]} as char(36))";
 | 
			
		||||
                else if (types[a].IsNumberType()) news[a] = $"cast({objs[a]} as varchar)";
 | 
			
		||||
                else news[a] = $"cast({objs[a]} as nvarchar(max))";
 | 
			
		||||
            }
 | 
			
		||||
            return string.Join(" + ", news);
 | 
			
		||||
        }
 | 
			
		||||
        public override string Mod(string left, string right, Type leftType, Type rightType) => $"{left} % {right}";
 | 
			
		||||
        public override string Div(string left, string right, Type leftType, Type rightType) => $"{left} / {right}";
 | 
			
		||||
        public override string Now => "getdate()";
 | 
			
		||||
        public override string NowUtc => "getutcdate()";
 | 
			
		||||
 | 
			
		||||
        public override string QuoteWriteParamterAdapter(Type type, string paramterName) => paramterName;
 | 
			
		||||
        protected override string QuoteReadColumnAdapter(Type type, Type mapType, string columnName) => columnName;
 | 
			
		||||
 | 
			
		||||
        public override string GetNoneParamaterSqlValue(List<DbParameter> specialParams, string specialParamFlag, ColumnInfo col, Type type, object value)
 | 
			
		||||
        {
 | 
			
		||||
            if (value == null) return "NULL";
 | 
			
		||||
            if (type.IsNumberType()) return string.Format(CultureInfo.InvariantCulture, "{0}", value);
 | 
			
		||||
            if (type == typeof(byte[])) return $"0x{CommonUtils.BytesSqlRaw(value as byte[])}";
 | 
			
		||||
            if (type == typeof(TimeSpan) || type == typeof(TimeSpan?))
 | 
			
		||||
            {
 | 
			
		||||
                var ts = (TimeSpan)value;
 | 
			
		||||
                value = $"{ts.Hours}:{ts.Minutes}:{ts.Seconds}.{ts.Milliseconds}";
 | 
			
		||||
            }
 | 
			
		||||
            return string.Format(CultureInfo.InvariantCulture, "{0}", (_orm.Ado as AdoProvider).AddslashesProcessParam(value, type, col));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user