using FreeSql.DataAnnotations; using FreeSql.Internal.Model; using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Data.Common; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; namespace FreeSql.Internal.CommonProvider { public abstract partial class Select0Provider { public int _limit, _skip; public string _select = "SELECT ", _orderby, _groupby, _having; public StringBuilder _where = new StringBuilder(); public List _params = new List(); public List _paramsInit; public List _tables = new List(); public List> _tableRules = new List>(); public Func _tableRule => _tableRules?.FirstOrDefault(); public Func _aliasRule; public string _tosqlAppendContent; public StringBuilder _join = new StringBuilder(); public IFreeSql _orm; public CommonUtils _commonUtils; public CommonExpression _commonExpression; public DbTransaction _transaction; public DbConnection _connection; public int _commandTimeout = 0; public Action _trackToList; public List> _includeToList = new List>(); #if net40 #else public List> _includeToListAsync = new List>(); #endif public Dictionary> _includeInfo = new Dictionary>(); public bool _distinct; public Expression _selectExpression; public List _whereGlobalFilter; public Func _cancel; public bool _is_AsTreeCte; public BaseDiyMemberExpression _diymemexpWithTempQuery; public Func _resolveHookTransaction; public bool IsDefaultSqlContent => _tables.Count == 1 && _tables[0].Table?.AsTableImpl == null && _distinct == false && _is_AsTreeCte == false && _where.Length == 0 && _join.Length == 0 && string.IsNullOrWhiteSpace(_orderby) && string.IsNullOrWhiteSpace(_groupby) && string.IsNullOrWhiteSpace(_tosqlAppendContent) && _aliasRule == null && _selectExpression == null; public Select0Provider() { _paramsInit = _params; } int _disposeCounter; ~Select0Provider() { if (Interlocked.Increment(ref _disposeCounter) != 1) return; _where.Clear(); if (_paramsInit == _params) _params.Clear(); //子查询与主查询共享,并发导致错误清除了主查询参数化信息 https://github.com/dotnetcore/FreeSql/issues/1155 _tables.Clear(); _tableRules.Clear(); _join.Clear(); _trackToList = null; _includeToList.Clear(); #if net40 #else _includeToListAsync.Clear(); #endif _includeInfo.Clear(); _selectExpression = null; _whereGlobalFilter?.Clear(); _cancel = null; _diymemexpWithTempQuery = null; } public abstract string ToSqlBase(string field = null); public abstract void AsTableBase(Func tableRule); public static void CopyData(Select0Provider from, Select0Provider to, ReadOnlyCollection lambParms) { if (to == null) return; to._limit = from._limit; to._skip = from._skip; to._select = from._select; to._orderby = from._orderby; to._groupby = from._groupby; to._having = from._having; to._where = new StringBuilder().Append(from._where.ToString()); to._params = new List(from._params.ToArray()); if (lambParms == null) { var fromParentTables = from._tables.Where(a => a.Type == SelectTableInfoType.Parent).ToList(); var fromTables = fromParentTables.Any() ? from._tables.Where(a => a.Type != SelectTableInfoType.Parent).ToList() : from._tables; if (to._tables.Count <= fromTables.Count) to._tables = new List(fromTables); else { to._tables = new List(to._tables); for (var a = 0; a < fromTables.Count; a++) to._tables[a] = fromTables[a]; } if (fromParentTables.Any()) to._tables.AddRange(fromParentTables); } else { var findedIndexs = new List(); var _multiTables = to._tables; _multiTables[0] = from._tables[0]; for (var a = 1; a < lambParms.Count; a++) { var tbIndex = from._tables.FindIndex(b => b.Alias == lambParms[a].Name && b.Table.Type == lambParms[a].Type); ; if (tbIndex != -1) { findedIndexs.Add(tbIndex); _multiTables[a] = from._tables[tbIndex]; } else { _multiTables[a].Alias = lambParms[a].Name; _multiTables[a].Parameter = lambParms[a]; } } for (var a = 1; a < from._tables.Count; a++) { if (findedIndexs.Contains(a)) continue; _multiTables.Add(from._tables[a]); } } to._tableRules = new List>(from._tableRules.ToArray()); to._aliasRule = from._aliasRule; to._join = new StringBuilder().Append(from._join.ToString()); //to._orm = from._orm; //to._commonUtils = from._commonUtils; //to._commonExpression = from._commonExpression; to._transaction = from._transaction; to._connection = from._connection; to._trackToList = from._trackToList; to._includeToList = new List>(from._includeToList.ToArray()); #if net40 #else to._includeToListAsync = new List>(from._includeToListAsync.ToArray()); #endif to._distinct = from._distinct; to._selectExpression = from._selectExpression; to._whereGlobalFilter = new List(from._whereGlobalFilter.ToArray()); to._cancel = from._cancel; to._is_AsTreeCte = from._is_AsTreeCte; to._diymemexpWithTempQuery = from._diymemexpWithTempQuery; } internal class WithTempQueryParser : BaseDiyMemberExpression { public List _insideSelectList = new List(); public List _outsideTable = new List(); public WithTempQueryParser(Select0Provider insideSelect, SelectGroupingProvider insideSelectGroup, Expression selector, SelectTableInfo outsideTable) { if (selector != null) { _insideSelectList.Add(new InsideInfo(insideSelect, insideSelectGroup, selector)); _outsideTable.Add(outsideTable); } } public class InsideInfo { public Select0Provider InsideSelect { get; } public SelectGroupingProvider InsideSelectGroup { get; } public CommonExpression InsideComonExp; public string InsideField { get; } public ReadAnonymousTypeAfInfo InsideAf { get; } public ReadAnonymousTypeInfo InsideMap { get; } public InsideInfo(Select0Provider insideSelect, SelectGroupingProvider insideSelectGroup, Expression selector) { InsideSelect = insideSelect; InsideSelectGroup = insideSelectGroup; InsideMap = new ReadAnonymousTypeInfo(); var field = new StringBuilder(); var index = CommonExpression.ReadAnonymousFieldAsCsName; //AsProperty if (selector != null) { if (insideSelectGroup != null) InsideSelect._commonExpression.ReadAnonymousField(null, insideSelect._tableRule, field, InsideMap, ref index, selector, insideSelect, InsideSelectGroup, null, null, null, false); else if (insideSelect != null) InsideSelect._commonExpression.ReadAnonymousField(insideSelect._tables, InsideSelect._tableRule, field, InsideMap, ref index, selector, null, insideSelect._diymemexpWithTempQuery, insideSelect._whereGlobalFilter, null, null, false); //不走 DTO 映射,不处理 IncludeMany } InsideField = field.Length > 0 ? field.Remove(0, 2).ToString() : null; InsideAf = new ReadAnonymousTypeAfInfo(InsideMap, "*"); field.Clear(); if (InsideMap.Childs.Where(a => a.Childs.Count > 1).Count() > 1) { var childs = InsideMap.GetAllChilds(); var duplicateNames = childs.GroupBy(a => a.DbNestedField).Where(a => a.Count() > 1).ToList(); if (duplicateNames.Count > 0) { foreach (var duplicateName in duplicateNames) { var dupmapIdx = 0; foreach (var dupmap in duplicateName) { if (++dupmapIdx == 1) continue; var newfield = insideSelect._commonUtils.TrimQuoteSqlName(dupmap.DbNestedField); while (InsideField.Contains($"{newfield}{dupmapIdx}")) ++dupmapIdx; dupmap.DbNestedField = insideSelect._commonUtils.QuoteSqlName($"{newfield}{dupmapIdx}"); } } foreach (var child in childs) field.Append(", ").Append(child.DbField).Append(InsideSelect._commonExpression.EndsWithDbNestedField(child.DbField, child.DbNestedField) ? "" : InsideSelect._commonUtils.FieldAsAlias(child.DbNestedField)); InsideField = field.Length > 0 ? field.Remove(0, 2).ToString() : null; field.Clear(); } } } } public WithTempQueryParser Append(Select0Provider select, SelectTableInfo outsideTable) { //select is ISelect if (outsideTable != null && select?._diymemexpWithTempQuery is WithTempQueryParser withTempQuery) { _insideSelectList.Add(withTempQuery._insideSelectList[0]); _outsideTable.Add(outsideTable); } return this; } public SelectTableInfo ParseExpMatchedTable { get; private set; } public override string ParseExp(Expression[] members) { ParseExpMapResult = null; ParseExpMatchedTable = GetOutsideSelectTable(members.FirstOrDefault()?.GetParameter()); if (ParseExpMatchedTable == null) return null; var insideIndex = _outsideTable.FindIndex(a => a == ParseExpMatchedTable); if (insideIndex == -1) { ParseExpMatchedTable = null; return null; } var insideData = _insideSelectList[insideIndex]; if (members.Any() == false) { ParseExpMapResult = insideData.InsideMap; return $"{ParseExpMatchedTable.Alias}.{insideData.InsideMap.DbNestedField}"; } var read = insideData.InsideMap; if (members.Length == 1 && members[0] == ParseExpMatchedTable.Parameter) { ParseExpMapResult = read; return $"{ParseExpMatchedTable.Alias}.{read.DbNestedField}"; } for (var a = members[0] == ParseExpMatchedTable.Parameter ? 1 : 0; a < members.Length; a++) { read = read.Childs.Where(z => z.CsName == (members[a] as MemberExpression)?.Member.Name).FirstOrDefault(); if (read == null) return null; } ParseExpMapResult = read; return $"{ParseExpMatchedTable.Alias}.{read.DbNestedField}"; } public SelectTableInfo GetOutsideSelectTable(ParameterExpression parameterExp) { if (parameterExp == null) return null; var find = _outsideTable.Where(a => a.Parameter == parameterExp).ToArray(); if (find.Length == 1) return find[0]; find = _outsideTable.Where(a => a.Table.Type == parameterExp.Type).ToArray(); if (find.Length == 1) return find[0]; find = _outsideTable.Where(a => a.Alias == parameterExp.Name).ToArray(); if (find.Length == 1) return find[0]; return null; } } public static MethodInfo _methodSqlExtInternalRawField = typeof(SqlExt).GetMethod("InternalRawField", BindingFlags.NonPublic | BindingFlags.Static); public static MethodInfo _methodSqlExtInternalRawSql = typeof(SqlExt).GetMethod("InternalRawSql", BindingFlags.NonPublic | BindingFlags.Static); public Expression ConvertStringPropertyToExpression(string property, bool fromFirstTable = false) { if (string.IsNullOrEmpty(property)) return null; var field = property.Split('.').Select(a => a.Trim()).ToArray(); Expression exp = null; if (field.Length == 1 && fromFirstTable == false) { if (_tables.Count == 1 && _tables[0].Table?.Type == typeof(object)) { //配合 .Select().WithSql("...").WhereDynamicFilter(...) var tb = _tables[0]; tb.Parameter = Expression.Parameter(tb.Table.Type, tb.Alias); var rawField = $"{tb.Alias}.{_commonUtils.QuoteSqlName(field[0])}"; return Expression.Call(_methodSqlExtInternalRawField, Expression.Constant(rawField, typeof(string))); } foreach (var tb in _tables) { if (tb.Table.ColumnsByCs.TryGetValue(field[0], out var col) && tb.Table.Properties.TryGetValue(field[0], out var prop)) { tb.Parameter = Expression.Parameter(tb.Table.Type, tb.Alias); exp = Expression.MakeMemberAccess(tb.Parameter, prop); break; } } if (exp == null) throw new Exception(CoreStrings.Cannot_Match_Property(property)); } else { var firstTb = _tables[0]; var firstTbs = _tables.Where(a => a.AliasInit == field[0]).ToArray(); if (firstTbs.Length == 1) firstTb = firstTbs[0]; else if (fromFirstTable == false) { firstTbs = _tables.Where(a => a.Table.Type.Name == field[0]).ToArray(); if (firstTbs.Length == 1) firstTb = firstTbs[0]; } firstTb.Parameter = Expression.Parameter(firstTb.Table.Type, firstTb.Alias); var currentType = firstTb.Table.Type; Expression currentExp = firstTb.Parameter; for (var x = firstTbs.Length == 1 ? 1 : 0; x < field.Length; x++) { var tmp1 = field[x]; if (_commonUtils.GetTableByEntity(currentType).Properties.TryGetValue(tmp1, out var prop) == false) throw new ArgumentException($"{currentType.DisplayCsharp()} {CoreStrings.NotFound_PropertyName(tmp1)}"); currentType = prop.PropertyType; currentExp = Expression.MakeMemberAccess(currentExp, prop); } exp = currentExp; } return exp; } public static MethodInfo _MethodDataReaderIsDBNull = typeof(DbDataReader).GetMethod("IsDBNull", new Type[] { typeof(int) }); public static Dictionary _dicMethodDataReaderGetValue = new Dictionary { [typeof(bool)] = typeof(DbDataReader).GetMethod("GetBoolean", new Type[] { typeof(int) }), [typeof(int)] = typeof(DbDataReader).GetMethod("GetInt32", new Type[] { typeof(int) }), [typeof(long)] = typeof(DbDataReader).GetMethod("GetInt64", new Type[] { typeof(int) }), [typeof(double)] = typeof(DbDataReader).GetMethod("GetDouble", new Type[] { typeof(int) }), [typeof(float)] = typeof(DbDataReader).GetMethod("GetFloat", new Type[] { typeof(int) }), [typeof(decimal)] = typeof(DbDataReader).GetMethod("GetDecimal", new Type[] { typeof(int) }), [typeof(DateTime)] = typeof(DbDataReader).GetMethod("GetDateTime", new Type[] { typeof(int) }), [typeof(string)] = typeof(DbDataReader).GetMethod("GetString", new Type[] { typeof(int) }), //[typeof(Guid)] = typeof(DbDataReader).GetMethod("GetGuid", new Type[] { typeof(int) }) 有些驱动不兼容 }; public static Dictionary> _dicMethodDataReaderGetValueOverride = new Dictionary>(); public static MethodInfo MethodStringContains = typeof(string).GetMethod("Contains", new[] { typeof(string) }); public static MethodInfo MethodStringStartsWith = typeof(string).GetMethod("StartsWith", new[] { typeof(string) }); public static MethodInfo MethodStringEndsWith = typeof(string).GetMethod("EndsWith", new[] { typeof(string) }); static ConcurrentDictionary MethodEnumerableDic = new ConcurrentDictionary(); public static MethodInfo GetMethodEnumerable(string methodName) => MethodEnumerableDic.GetOrAdd(methodName, et => { var methods = typeof(Enumerable).GetMethods().Where(a => a.Name == et); if (et == "Select") return methods.Where(a => a.GetParameters()[1].ParameterType.GetGenericTypeDefinition() == typeof(Func<,>)).FirstOrDefault(); return methods.FirstOrDefault(); }); public List> _SameSelectPendingShareData; internal Select0Provider SetSameSelectPendingShareData(List> data) { _SameSelectPendingShareData = data; //_SameSelectPendingShareData?.ForEach(a => _params.AddRange(a?.Item2 ?? new DbParameter[0])); #1205 SqlServer BUG 参数化重复 if (_SameSelectPendingShareData?.Any() == true) { var last = _SameSelectPendingShareData.Last(); if (last == null && _SameSelectPendingShareData.Count > 1) last = _SameSelectPendingShareData[_SameSelectPendingShareData.Count - 2]; if (last != null) _params.AddRange(last.Item2 ?? new DbParameter[0]); } return this; } internal bool SameSelectPending(ref string sql, ReadAnonymousTypeOtherInfo csspsod) { if (_SameSelectPendingShareData != null) { if (_SameSelectPendingShareData.Any() == false || _SameSelectPendingShareData.Last() != null) { _SameSelectPendingShareData.Add(NativeTuple.Create(sql, _params.ToArray(), csspsod)); return true; } _SameSelectPendingShareData[_SameSelectPendingShareData.Count - 1] = NativeTuple.Create(sql, _params.ToArray(), csspsod); var sbSql = new StringBuilder(); //last == null flush flag for (var a = 0; a < _SameSelectPendingShareData.Count; a++) sbSql.Append("\r\nUNION ALL\r\nselect * from (").Append(_SameSelectPendingShareData[a].Item1).Append(") ftb"); sbSql.Remove(0, 13); if (_SameSelectPendingShareData.Count == 1) sbSql.Remove(0, 15).Remove(sbSql.Length - 5, 5); sql = sbSql.ToString(); //dbParms = cssps.Select(a => a.Item2).SelectMany(a => a).ToArray(); sbSql.Clear(); } return false; } internal static Expression SetSameSelectPendingShareDataWithExpression(Expression exp, List> data) { var callExp = exp as MethodCallExpression; var callExpStack = new Stack(); callExpStack.Push(callExp); callExp = callExp.Object as MethodCallExpression; //忽略第一个方法 while (callExp != null) { if (callExp?.Type.FullName.StartsWith("FreeSql.ISelect`") == true) { callExpStack.Push(callExp); callExp = callExp.Object as MethodCallExpression; continue; } return exp; } callExp = callExpStack.Pop(); Expression newExp = Expression.Call( Expression.Convert(callExp, typeof(Select0Provider)), typeof(Select0Provider).GetMethod(nameof(SetSameSelectPendingShareData), BindingFlags.NonPublic | BindingFlags.Instance), Expression.Constant(data, typeof(List>)) ); newExp = Expression.Convert(newExp, callExp.Type); while (callExpStack.Any()) { callExp = callExpStack.Pop(); newExp = Expression.Call( newExp, callExp.Method, callExp.Arguments ); } return newExp; } public ReadAnonymousTypeAfInfo GetExpressionField(Expression newexp, FieldAliasOptions fieldAlias = FieldAliasOptions.AsIndex) { var map = new ReadAnonymousTypeInfo(); var field = new StringBuilder(); var index = fieldAlias == FieldAliasOptions.AsProperty ? CommonExpression.ReadAnonymousFieldAsCsName : 0; _commonExpression.ReadAnonymousField(_tables, _tableRule, field, map, ref index, newexp, this, _diymemexpWithTempQuery, _whereGlobalFilter, null, null, true); return new ReadAnonymousTypeAfInfo(map, field.Length > 0 ? field.Remove(0, 2).ToString() : null); } public string GetNestSelectSql(Expression select, string affield, Func ToSql) { var allMemExps = new FindAllMemberExpressionVisitor(this); allMemExps.Visit(select); var fieldAlias = new Dictionary(); var fieldReplaced = new Dictionary(); var field = new StringBuilder(); foreach (var memExp in allMemExps.Result) { var gef = GetExpressionField(memExp.Item1, FieldAliasOptions.AsProperty); var geffield = gef.field; if (fieldReplaced.ContainsKey(geffield)) continue; fieldReplaced.Add(geffield, true); field.Append(", ").Append(gef.field); if (fieldAlias.ContainsKey(memExp.Item2.Attribute.Name)) { field.Append(_commonUtils.FieldAsAlias($"aas{fieldAlias.Count}")); affield = affield.Replace(geffield, $"ftba.aas{fieldAlias.Count}"); } else { fieldAlias.Add(memExp.Item2.Attribute.Name, true); affield = affield.Replace(geffield, $"ftba.{string.Join(".", geffield.Split('.').Skip(1))}"); } } var sql = ToSql(field.Remove(0, 2).ToString()); sql = $"{_select} {affield} FROM ( \r\n {sql.Replace("\r\n", "\r\n ")}\r\n) ftba"; field.Clear(); return sql; } public class FindAllMemberExpressionVisitor : ExpressionVisitor { public List> Result { get; set; } = new List>(); Select0Provider _select; public FindAllMemberExpressionVisitor(Select0Provider select) => _select = select; protected override Expression VisitMember(MemberExpression node) { var exps = new Stack(); Expression exp = node; while (exp != null) { switch (exp.NodeType) { case ExpressionType.Parameter: exps.Push(exp); exp = null; continue; case ExpressionType.MemberAccess: exps.Push(exp); exp = (exp as MemberExpression)?.Expression; continue; } return base.VisitMember(node); } if (exps.Any() == false) return base.VisitMember(node); var firstExp = exps.Pop() as ParameterExpression; if (firstExp == null) return base.VisitMember(node); var tb = _select._tables.Find(a => a.Parameter == firstExp)?.Table; if (tb == null) return base.VisitMember(node); if (tb.Columns.Any() == false && _select._diymemexpWithTempQuery != null) //匿名类,嵌套查询 DTO { Result.Add(NativeTuple.Create(node, default(ColumnInfo))); return node; } while (exps.Any()) { var memExp = exps.Pop() as MemberExpression; if (tb.ColumnsByCs.TryGetValue(memExp.Member.Name, out var trycol) && exps.Any() == false) { Result.Add(NativeTuple.Create(node, trycol)); return node; } if (tb.Properties.ContainsKey(memExp.Member.Name)) { tb = _select._commonUtils.GetTableByEntity(memExp.Type); if (tb == null) return base.VisitMember(node); } } return base.VisitMember(node); } } public class ReplaceMemberExpressionVisitor : ExpressionVisitor { Expression _findExp; Expression _replaceExp; public Expression Replace(Expression exp, Expression find, Expression replace) // object repval) { _findExp = find; _replaceExp = replace; //_replaceExp = Expression.Constant(repval, find.Type); return this.Visit(exp); } protected override Expression VisitMember(MemberExpression node) { if (_findExp == node) return _replaceExp; if (node.Expression?.NodeType == ExpressionType.Parameter && node.Expression == _findExp) return Expression.Property(_replaceExp, node.Member.Name); return base.VisitMember(node); } } } public abstract partial class Select0Provider : Select0Provider, ISelect0 where TSelect : class { public Select0Provider(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) { _orm = orm; _commonUtils = commonUtils; _commonExpression = commonExpression; _tables.Add(new SelectTableInfo { Table = _commonUtils.GetTableByEntity(typeof(T1)), Alias = "a", On = null, Type = SelectTableInfoType.From }); this.Where(_commonUtils.WhereObject(_tables.First().Table, "a.", dywhere)); if (_orm.CodeFirst.IsAutoSyncStructure && typeof(T1) != typeof(object)) _orm.CodeFirst.SyncStructure(); } public TSelect TrackToList(Action track) { _trackToList = track; return this as TSelect; } public TSelect Cancel(Func cancel) { _cancel = cancel; return this as TSelect; } public TSelect WithTransaction(DbTransaction transaction) { _transaction = transaction; if (transaction != null) _connection = transaction.Connection; return this as TSelect; } public TSelect WithConnection(DbConnection connection) { if (_transaction?.Connection != connection) _transaction = null; _connection = connection; return this as TSelect; } public TSelect WithParameters(List parameters) { if (parameters != null) _params = parameters; return this as TSelect; } public TSelect CommandTimeout(int timeout) { _commandTimeout = timeout; return this as TSelect; } public TSelect GroupBy(string sql, object parms = null) { _groupby = sql; if (string.IsNullOrEmpty(_groupby)) return this as TSelect; _groupby = string.Concat(" \r\nGROUP BY ", _groupby); if (parms != null) _params.AddRange(_commonUtils.GetDbParamtersByObject(_groupby, parms)); return this as TSelect; } public TSelect Having(string sql, object parms = null) { if (string.IsNullOrEmpty(_groupby) || string.IsNullOrEmpty(sql)) return this as TSelect; _having = string.Concat(_having, " AND (", sql, ")"); if (parms != null) _params.AddRange(_commonUtils.GetDbParamtersByObject(sql, parms)); return this as TSelect; } public TSelect LeftJoin(Expression> exp) { if (exp == null) return this as TSelect; _tables[0].Parameter = exp.Parameters[0]; return this.InternalJoin(exp?.Body, SelectTableInfoType.LeftJoin); } public TSelect InnerJoin(Expression> exp) { if (exp == null) return this as TSelect; _tables[0].Parameter = exp.Parameters[0]; return this.InternalJoin(exp?.Body, SelectTableInfoType.InnerJoin); } public TSelect RightJoin(Expression> exp) { if (exp == null) return this as TSelect; _tables[0].Parameter = exp.Parameters[0]; return this.InternalJoin(exp?.Body, SelectTableInfoType.RightJoin); } public TSelect LeftJoin(Expression> exp) { if (exp == null) return this as TSelect; _tables[0].Parameter = exp.Parameters[0]; if (_tables.Count > 1 && _tables[1].Table.Type == typeof(T2)) _tables[1].Parameter = exp.Parameters[1]; return this.InternalJoin(exp?.Body, SelectTableInfoType.LeftJoin); } public TSelect InnerJoin(Expression> exp) { if (exp == null) return this as TSelect; _tables[0].Parameter = exp.Parameters[0]; if (_tables.Count > 1 && _tables[1].Table.Type == typeof(T2)) _tables[1].Parameter = exp.Parameters[1]; return this.InternalJoin(exp?.Body, SelectTableInfoType.InnerJoin); } public TSelect RightJoin(Expression> exp) { if (exp == null) return this as TSelect; _tables[0].Parameter = exp.Parameters[0]; if (_tables.Count > 1 && _tables[1].Table.Type == typeof(T2)) _tables[1].Parameter = exp.Parameters[1]; return this.InternalJoin(exp?.Body, SelectTableInfoType.RightJoin); } public TSelect InnerJoin(string sql, object parms = null) { if (string.IsNullOrEmpty(sql)) return this as TSelect; _join.Append(" \r\nINNER JOIN ").Append(sql); if (parms != null) _params.AddRange(_commonUtils.GetDbParamtersByObject(sql, parms)); return this as TSelect; } public TSelect LeftJoin(string sql, object parms = null) { if (string.IsNullOrEmpty(sql)) return this as TSelect; _join.Append(" \r\nLEFT JOIN ").Append(sql); if (parms != null) _params.AddRange(_commonUtils.GetDbParamtersByObject(sql, parms)); return this as TSelect; } public TSelect RightJoin(string sql, object parms = null) { if (string.IsNullOrEmpty(sql)) return this as TSelect; _join.Append(" \r\nRIGHT JOIN ").Append(sql); if (parms != null) _params.AddRange(_commonUtils.GetDbParamtersByObject(sql, parms)); return this as TSelect; } public TSelect RawJoin(string sql) { if (string.IsNullOrEmpty(sql)) return this as TSelect; _join.Append(" \r\n").Append(sql); //fsql.Select().RawJoin("FULL JOIN UserGroup b ON b.id = a.GroupId").ToSql((a, b) => new { user = a, group = b }); for (var a = 1; a < _tables.Count; a++) { var tb = _tables[a]; if (sql.Contains($" {tb.Table.DbName} ") || sql.Contains($" {_commonUtils.QuoteSqlName(tb.Table.DbName)} ")) tb.Type = SelectTableInfoType.RawJoin; } return this as TSelect; } public TSelect Limit(int limit) { _limit = limit; return this as TSelect; } public TSelect Master() { _select = $" {_select.Trim()} "; return this as TSelect; } public TSelect Offset(int offset) => this.Skip(offset) as TSelect; public TSelect OrderBy(string sql, object parms = null) => this.OrderBy(true, sql, parms); public TSelect OrderBy(bool condition, string sql, object parms = null) { if (condition == false) return this as TSelect; if (string.IsNullOrEmpty(sql)) { _orderby = null; return this as TSelect; } var isnull = string.IsNullOrEmpty(_orderby); _orderby = string.Concat(isnull ? " \r\nORDER BY " : "", _orderby, isnull ? "" : ", ", sql); if (parms != null) _params.AddRange(_commonUtils.GetDbParamtersByObject(sql, parms)); return this as TSelect; } public TSelect Page(int pageNumber, int pageSize) { this.Skip(Math.Max(0, pageNumber - 1) * pageSize); return this.Limit(pageSize) as TSelect; } public TSelect Page(BasePagingInfo pagingInfo) { pagingInfo.Count = this.Count(); this.Skip(Math.Max(0, pagingInfo.PageNumber - 1) * pagingInfo.PageSize); return this.Limit(pagingInfo.PageSize) as TSelect; } public TSelect Skip(int offset) { _skip = offset; return this as TSelect; } public TSelect Take(int limit) => this.Limit(limit) as TSelect; public TSelect Distinct() { _distinct = true; return this as TSelect; } string GetToDeleteWhere(string alias) { var pks = _tables[0].Table.Primarys; var old_selectVal = _select; switch (_orm.Ado.DataType) { case DataType.Dameng: case DataType.OdbcDameng: //达梦不能这样 case DataType.Oracle: case DataType.OdbcOracle: case DataType.CustomOracle: case DataType.Firebird: case DataType.GBase: break; default: _select = "SELECT "; break; } try { if (pks.Length == 1) return $"{_commonUtils.QuoteSqlName(_tables[0].Table.Primarys[0].Attribute.Name)} in (select * from ({this.ToSql($"{_tables[0].Alias}.{_commonUtils.QuoteSqlName(_tables[0].Table.Primarys[0].Attribute.Name)}")}) {alias})"; else { var concatTypes = new Type[pks.Length * 2 - 1]; var concatMainCols = new string[pks.Length * 2 - 1]; var concatInCols = new string[pks.Length * 2 - 1]; var concatSplit = _commonUtils.FormatSql("{0}", $",{alias},"); for (var a = 0; a < pks.Length; a++) { concatTypes[a * 2] = pks[a].CsType; concatMainCols[a * 2] = _commonUtils.QuoteSqlName(pks[a].Attribute.Name); concatInCols[a * 2] = $"{_tables[0].Alias}.{_commonUtils.QuoteSqlName(pks[a].Attribute.Name)}"; if (a < pks.Length - 1) { concatTypes[a * 2 + 1] = typeof(string); concatMainCols[a * 2 + 1] = concatSplit; concatInCols[a * 2 + 1] = concatSplit; } } return $"{_commonUtils.StringConcat(concatMainCols, concatTypes)} in (select * from ({this.ToSql($"{_commonUtils.StringConcat(concatInCols, concatTypes)} as as1")}) {alias})"; } } finally { _select = old_selectVal; } } public IDelete ToDelete() { if (_tables[0].Table.Primarys.Any() == false) throw new Exception(CoreStrings.Entity_Must_Primary_Key("ToDelete", _tables[0].Table.CsName)); var del = (_orm as BaseDbProvider).CreateDeleteProvider(null) as DeleteProvider; if (_tables[0].Table.Type != typeof(T1)) del.AsType(_tables[0].Table.Type); if (_params.Any()) del._params = new List(_params.ToArray()); if (_whereGlobalFilter.Any()) del._whereGlobalFilter = new List(_whereGlobalFilter.ToArray()); del.WithConnection(_connection).WithTransaction(_transaction ?? _orm.Ado.TransactionCurrentThread ?? _resolveHookTransaction?.Invoke()).CommandTimeout(_commandTimeout); if (_is_AsTreeCte == false) { var trytbname = ""; del.AsTable(old => GetTableRuleUnions().FirstOrDefault()?.TryGetValue(_tables[0].Table.Type, out trytbname) == true && trytbname.IndexOf(' ') == -1 ? trytbname : null); } switch (_orm.Ado.DataType) { case DataType.Dameng: case DataType.OdbcDameng: //达梦不能这样 case DataType.Oracle: case DataType.OdbcOracle: case DataType.CustomOracle: case DataType.Firebird: case DataType.GBase: break; default: var beforeSql = this._select; if (beforeSql.EndsWith("SELECT ", StringComparison.OrdinalIgnoreCase)) { beforeSql = beforeSql.Substring(0, beforeSql.Length - 7); if (string.IsNullOrEmpty(beforeSql) == false) del._interceptSql = sb => sb.Insert(0, beforeSql); } break; } return del.Where(GetToDeleteWhere("ftb_del")); } public IUpdate ToUpdate() { if (_tables[0].Table.Primarys.Any() == false) throw new Exception(CoreStrings.Entity_Must_Primary_Key("ToUpdate", _tables[0].Table.CsName)); var upd = (_orm as BaseDbProvider).CreateUpdateProvider(null) as UpdateProvider; if (_tables[0].Table.Type != typeof(T1)) upd.AsType(_tables[0].Table.Type); if (_params.Any()) upd._params = new List(_params.ToArray()); if (_whereGlobalFilter.Any()) upd._whereGlobalFilter = new List(_whereGlobalFilter.ToArray()); upd.WithConnection(_connection).WithTransaction(_transaction ?? _orm.Ado.TransactionCurrentThread ?? _resolveHookTransaction?.Invoke()).CommandTimeout(_commandTimeout); if (_is_AsTreeCte == false) { var trytbname = ""; upd.AsTable(old => GetTableRuleUnions().FirstOrDefault()?.TryGetValue(_tables[0].Table.Type, out trytbname) == true && trytbname.IndexOf(' ') == -1 ? trytbname : null); } switch (_orm.Ado.DataType) { case DataType.Dameng: case DataType.OdbcDameng: //达梦不能这样 case DataType.Oracle: case DataType.OdbcOracle: case DataType.CustomOracle: case DataType.Firebird: case DataType.GBase: break; default: var beforeSql = this._select; if (beforeSql.EndsWith("SELECT ", StringComparison.OrdinalIgnoreCase)) { beforeSql = beforeSql.Substring(0, beforeSql.Length - 7); if (string.IsNullOrEmpty(beforeSql) == false) upd._interceptSql = sb => sb.Insert(0, beforeSql); } break; } return upd.Where(GetToDeleteWhere("ftb_upd")); } protected List> GetTableRuleUnions() { var unions = new List>(); var trs = _tableRules.Any() ? _tableRules : new List>(new[] { new Func((type, oldname) => null) }); if (trs.Count == 1 && _tables.Any(a => a.Table != null && a.Table.AsTableImpl != null && string.IsNullOrWhiteSpace(trs[0](a.Table.Type, a.Table.AsTableImpl != null ? null : a.Table.DbName)) == true)) { DateTime? DateTimeAsTableImplStart = null, DateTimeAsTableImplEnd = null; string[] LocalGetTableNames(SelectTableInfo tb) { var trname = trs[0](tb.Table.Type, tb.Table.AsTableImpl != null ? null : tb.Table.DbName); if (tb.Table.AsTableImpl != null && string.IsNullOrWhiteSpace(trname) == true) { var aret = tb.Table.AsTableImpl.GetTableNamesBySqlWhere(_where.Length == 0 ? null : _where.ToString(), _params, tb, _commonUtils); if (aret.Names.Any() == false) { var now = DateTime.Now; aret = new IAsTableTableNameRangeResult(tb.Table.AsTableImpl.AllTables.Take(1).ToArray(), now, now); } for (var a = 0; a < aret.Names.Length; a++) { if (_orm.CodeFirst.IsSyncStructureToLower) aret.Names[a] = aret.Names[a].ToLower(); if (_orm.CodeFirst.IsSyncStructureToUpper) aret.Names[a] = aret.Names[a].ToUpper(); if (_orm.CodeFirst.IsAutoSyncStructure) _orm.CodeFirst.SyncStructure(tb.Table.Type, aret.Names[a]); } if (tb.Table.AsTableImpl is DateTimeAsTableImpl) { if (aret.ColumnValue1 is DateTime dt1) if (DateTimeAsTableImplStart == null || dt1 > DateTimeAsTableImplStart) DateTimeAsTableImplStart = dt1; if (aret.ColumnValue2 is DateTime dt2) if (DateTimeAsTableImplEnd == null || dt2 < DateTimeAsTableImplEnd) DateTimeAsTableImplEnd = dt2; } return aret.Names; } if (string.IsNullOrWhiteSpace(trname) == false) { if (trname.IndexOf(' ') == -1) //还可以这样:select.AsTable((a, b) => "(select * from tb_topic where clicks > 10)").Page(1, 10).ToList() { if (_orm.CodeFirst.IsSyncStructureToLower) trname = trname.ToLower(); if (_orm.CodeFirst.IsSyncStructureToUpper) trname = trname.ToUpper(); if (_orm.CodeFirst.IsAutoSyncStructure) _orm.CodeFirst.SyncStructure(tb.Table.Type, trname); } else trname = trname.Replace(" \r\n", " \r\n "); return new string[] { trname }; } return new string[] { tb.Table.DbName }; } var tbnames = _tables.Where(a => a.Type != SelectTableInfoType.Parent).GroupBy(a => a.Table.Type).Select(g => _tables.Where(a => a.Table.Type == g.Key).FirstOrDefault()).Select(a => new { Tb = a, Names = LocalGetTableNames(a) }).ToList(); if (DateTimeAsTableImplStart != null && DateTimeAsTableImplEnd != null && tbnames.Where(a => a.Names.Length > 1).Count() > 1) { tbnames = tbnames.Select(a => new { a.Tb, Names = a.Tb.Table.AsTableImpl?.GetTableNamesByColumnValueRange(DateTimeAsTableImplStart, DateTimeAsTableImplEnd) ?? a.Names }).ToList(); } var dict = new Dictionary(); tbnames.ForEach(a => { dict.Add(a.Tb.Table.Type, a.Names[0]); }); unions.Add(dict); for (var a = 0; a < tbnames.Count; a++) { if (tbnames[a].Names.Length <= 1) continue; var unionsCount = unions.Count; for (var b = 1; b < tbnames[a].Names.Length; b++) { for (var d = 0; d < unionsCount; d++) { dict = new Dictionary(); foreach (var uit in unions[d]) dict.Add(uit.Key, uit.Key == tbnames[a].Tb.Table.Type ? tbnames[a].Names[b] : uit.Value); unions.Add(dict); } } } if (DateTimeAsTableImplStart != null && DateTimeAsTableImplEnd != null && tbnames.Where(a => a.Names.Length > 1).Count() > 1) { for (var uidx = unions.Count - 1; uidx >= 0; uidx--) { var ignore = false; DateTime? dtStart = null, dtEnd = null; foreach (var ut in unions[uidx]) { if (tbnames.Where(a => a.Tb.Table.Type == ut.Key).FirstOrDefault()?.Tb.Table?.AsTableImpl is DateTimeAsTableImpl dtImpl == false) continue; var dtRange = dtImpl.GetRangeByTableName(ut.Value); if (dtRange == null) continue; if (dtStart == null) dtStart = dtRange.Item1; if (dtEnd == null) dtEnd = dtRange.Item2; if (dtRange.Item1 >= dtEnd || dtRange.Item2 <= dtStart) { ignore = true; break; } if (dtRange.Item1 > dtStart) dtStart = dtRange.Item1; if (dtRange.Item2 < dtEnd) dtEnd = dtRange.Item2; } if (ignore) unions.RemoveAt(uidx); } } return unions; } if (trs.Any() == false) trs.Add(new Func((type, oldname) => null)); foreach (var tr in trs) { var dict = new Dictionary(); foreach (var tb in _tables) { if (tb.Type == SelectTableInfoType.Parent) continue; if (dict.ContainsKey(tb.Table.Type)) continue; var name = tr?.Invoke(tb.Table.Type, tb.Table.DbName); if (string.IsNullOrEmpty(name)) name = tb.Table.DbName; else { if (name.IndexOf(' ') == -1) //还可以这样:select.AsTable((a, b) => "(select * from tb_topic where clicks > 10)").Page(1, 10).ToList() { if (_orm.CodeFirst.IsSyncStructureToLower) name = name.ToLower(); if (_orm.CodeFirst.IsSyncStructureToUpper) name = name.ToUpper(); if (_orm.CodeFirst.IsAutoSyncStructure) _orm.CodeFirst.SyncStructure(tb.Table.Type, name); } else name = name.Replace(" \r\n", " \r\n "); } dict.Add(tb.Table.Type, name); } unions.Add(dict); } return unions; } public override void AsTableBase(Func tableRule) => AsTable(tableRule); public TSelect AsTable(Func tableRule) { if (_tableRules.Count == 1 && _diymemexpWithTempQuery != null && _diymemexpWithTempQuery is WithTempQueryParser tempQueryParser) { var oldTableRule = _tableRules[0]; var newTableRule = tableRule; _tableRules.Clear(); tableRule = (type, old) => { old = oldTableRule(type, old); var newname = newTableRule(type, old); return string.IsNullOrWhiteSpace(newname) ? old : newname; }; } if (tableRule != null) _tableRules.Add(tableRule); return this as TSelect; } public TSelect AsAlias(Func aliasRule) { if (aliasRule != null) _aliasRule = aliasRule; return this as TSelect; } public TSelect AsType(Type entityType) { if (entityType == typeof(object)) throw new Exception(CoreStrings.TypeAsType_NotSupport_Object("ISelect")); if (entityType == _tables[0].Table.Type) return this as TSelect; var newtb = _commonUtils.GetTableByEntity(entityType); _tables[0].Table = newtb ?? throw new Exception(CoreStrings.Type_AsType_Parameter_Error("ISelect")); if (_orm.CodeFirst.IsAutoSyncStructure) _orm.CodeFirst.SyncStructure(entityType); return this as TSelect; } public override string ToSqlBase(string field = null) => ToSql(field); public abstract string ToSql(string field = null); public TSelect Where(string sql, object parms = null) => this.WhereIf(true, sql, parms); public TSelect WhereIf(bool condition, string sql, object parms = null) { if (condition == false || string.IsNullOrEmpty(sql)) return this as TSelect; _where.Append(" AND (").Append(sql).Append(")"); if (parms != null) _params.AddRange(_commonUtils.GetDbParamtersByObject(sql, parms)); return this as TSelect; } public TSelect OrderByPropertyName(string property, bool isAscending = true) => OrderByPropertyNameIf(true, property, isAscending); public TSelect OrderByPropertyNameIf(bool condition, string property, bool isAscending = true) { if (condition == false) return this as TSelect; Expression exp = ConvertStringPropertyToExpression(property); if (exp == null) return this as TSelect; var field = _commonExpression.ExpressionSelectColumn_MemberAccess(_tables, _tableRule, null, SelectTableInfoType.From, exp, true, _diymemexpWithTempQuery); if (isAscending) return this.OrderBy(field); return this.OrderBy($"{field} DESC"); } public TSelect WhereDynamicFilter(DynamicFilterInfo filter) { if (filter == null) return this as TSelect; var sb = new StringBuilder(); if (IsIgnoreFilter(filter)) filter.Field = ""; ParseFilter(DynamicFilterLogic.And, filter, true); this.Where(sb.ToString()); sb.Clear(); return this as TSelect; void ParseFilter(DynamicFilterLogic logic, DynamicFilterInfo fi, bool isend) { if (string.IsNullOrEmpty(fi.Field) == false) { Expression exp = null; switch (fi.Operator) { case DynamicFilterOperator.Custom: var fiValueCustomArray = fi.Field?.ToString().Split(new[] { ' ' }, 2); if (fiValueCustomArray.Length != 2) throw new ArgumentException(CoreStrings.CustomFieldSeparatedBySpaces); if (string.IsNullOrWhiteSpace(fiValueCustomArray[0])) throw new ArgumentException(CoreStrings.Custom_StaticMethodName_IsNotNull); if (string.IsNullOrWhiteSpace(fiValueCustomArray[1])) throw new ArgumentException(CoreStrings.Custom_Reflection_IsNotNull); var fiValue1Type = Type.GetType(fiValueCustomArray[1]); if (fiValue1Type == null) throw new ArgumentException(CoreStrings.NotFound_Reflection(fiValueCustomArray[1])); var fiValue0Method = fiValue1Type.GetMethod(fiValueCustomArray[0], new Type[] { typeof(string) }); if (fiValue0Method == null) fiValue0Method = fiValue1Type.GetMethod(fiValueCustomArray[0], new Type[] { typeof(object), typeof(string) }); if (fiValue0Method == null) throw new ArgumentException(CoreStrings.NotFound_Static_MethodName(fiValueCustomArray[0])); if (MethodIsDynamicFilterCustomAttribute(fiValue0Method) == false) throw new ArgumentException(CoreStrings.Custom_StaticMethodName_NotSet_DynamicFilterCustom(fiValueCustomArray[0])); var fiValue0MethodReturn = fiValue0Method?.Invoke(null, fiValue0Method.GetParameters() .Select(a => a.ParameterType == typeof(object) ? (object)this : (a.ParameterType == typeof(string) ? (object)(fi.Value?.ToString()) : (object)null)) .ToArray()); exp = fiValue0MethodReturn is Expression expression ? expression : Expression.Call(_methodSqlExtInternalRawSql, Expression.Constant(fiValue0MethodReturn?.ToString(), typeof(string))); break; case DynamicFilterOperator.Contains: case DynamicFilterOperator.StartsWith: case DynamicFilterOperator.EndsWith: case DynamicFilterOperator.NotContains: case DynamicFilterOperator.NotStartsWith: case DynamicFilterOperator.NotEndsWith: exp = ConvertStringPropertyToExpression(fi.Field); if (exp.Type != typeof(string)) exp = Expression.TypeAs(exp, typeof(string)); break; default: exp = ConvertStringPropertyToExpression(fi.Field); if (exp.Type == typeof(object) && fi.Value != null) { var valueType = fi.Value?.GetType(); if (Utils.dicExecuteArrayRowReadClassOrTuple.ContainsKey(valueType)) exp = Expression.Convert(exp, valueType); else if (valueType.FullName == "System.Text.Json.JsonElement") { var valueKind = valueType.GetProperty("ValueKind").GetValue(fi.Value, null).ToString(); if (valueKind == "Number") exp = Expression.Convert(exp, typeof(decimal)); } } break; } switch (fi.Operator) { case DynamicFilterOperator.Contains: exp = Expression.Call(exp, MethodStringContains, Expression.Constant(Utils.GetDataReaderValue(exp.Type, fi.Value?.ToString()), exp.Type)); break; case DynamicFilterOperator.StartsWith: exp = Expression.Call(exp, MethodStringStartsWith, Expression.Constant(Utils.GetDataReaderValue(exp.Type, fi.Value?.ToString()), exp.Type)); break; case DynamicFilterOperator.EndsWith: exp = Expression.Call(exp, MethodStringEndsWith, Expression.Constant(Utils.GetDataReaderValue(exp.Type, fi.Value?.ToString()), exp.Type)); break; case DynamicFilterOperator.NotContains: exp = Expression.Not(Expression.Call(exp, MethodStringContains, Expression.Constant(Utils.GetDataReaderValue(exp.Type, fi.Value?.ToString()), exp.Type))); break; case DynamicFilterOperator.NotStartsWith: exp = Expression.Not(Expression.Call(exp, MethodStringStartsWith, Expression.Constant(Utils.GetDataReaderValue(exp.Type, fi.Value?.ToString()), exp.Type))); break; case DynamicFilterOperator.NotEndsWith: exp = Expression.Not(Expression.Call(exp, MethodStringEndsWith, Expression.Constant(Utils.GetDataReaderValue(exp.Type, fi.Value?.ToString()), exp.Type))); break; case DynamicFilterOperator.Eq: case DynamicFilterOperator.Equals: case DynamicFilterOperator.Equal: exp = Expression.Equal(exp, Expression.Constant(Utils.GetDataReaderValue(exp.Type, fi.Value?.ToString()), exp.Type)); break; case DynamicFilterOperator.NotEqual: exp = Expression.NotEqual(exp, Expression.Constant(Utils.GetDataReaderValue(exp.Type, fi.Value?.ToString()), exp.Type)); break; case DynamicFilterOperator.GreaterThan: exp = Expression.Call(typeof(SqlExt).GetMethod("GreaterThan").MakeGenericMethod(exp.Type), exp, Expression.Constant(Utils.GetDataReaderValue(exp.Type, fi.Value?.ToString()), exp.Type)); break; case DynamicFilterOperator.GreaterThanOrEqual: exp = Expression.Call(typeof(SqlExt).GetMethod("GreaterThanOrEqual").MakeGenericMethod(exp.Type), exp, Expression.Constant(Utils.GetDataReaderValue(exp.Type, fi.Value?.ToString()), exp.Type)); break; case DynamicFilterOperator.LessThan: exp = Expression.Call(typeof(SqlExt).GetMethod("LessThan").MakeGenericMethod(exp.Type), exp, Expression.Constant(Utils.GetDataReaderValue(exp.Type, fi.Value?.ToString()), exp.Type)); break; case DynamicFilterOperator.LessThanOrEqual: exp = Expression.Call(typeof(SqlExt).GetMethod("LessThanOrEqual").MakeGenericMethod(exp.Type), exp, Expression.Constant(Utils.GetDataReaderValue(exp.Type, fi.Value?.ToString()), exp.Type)); break; case DynamicFilterOperator.Range: var fiValueRangeArray = getFiListValue(); if (fiValueRangeArray.Length != 2) throw new ArgumentException(CoreStrings.Range_Comma_Separateda_By2Char); exp = Expression.AndAlso( Expression.GreaterThanOrEqual(exp, Expression.Constant(Utils.GetDataReaderValue(exp.Type, fiValueRangeArray[0]), exp.Type)), Expression.LessThan(exp, Expression.Constant(Utils.GetDataReaderValue(exp.Type, fiValueRangeArray[1]), exp.Type))); break; case DynamicFilterOperator.DateRange: var fiValueDateRangeArray = getFiListValue(); if (fiValueDateRangeArray?.Length != 2) throw new ArgumentException(CoreStrings.DateRange_Comma_Separateda_By2Char); if (Regex.IsMatch(fiValueDateRangeArray[1], @"^\d\d\d\d[\-/]\d\d?[\-/]\d\d?$")) fiValueDateRangeArray[1] = DateTime.Parse(fiValueDateRangeArray[1]).AddDays(1).ToString("yyyy-MM-dd HH:mm:ss"); else if (Regex.IsMatch(fiValueDateRangeArray[1], @"^\d\d\d\d[\-/]\d\d?$")) fiValueDateRangeArray[1] = DateTime.Parse($"{fiValueDateRangeArray[1]}-01").AddMonths(1).ToString("yyyy-MM-dd HH:mm:ss"); else if (Regex.IsMatch(fiValueDateRangeArray[1], @"^\d\d\d\d$")) fiValueDateRangeArray[1] = DateTime.Parse($"{fiValueDateRangeArray[1]}-01-01").AddYears(1).ToString("yyyy-MM-dd HH:mm:ss"); else if (Regex.IsMatch(fiValueDateRangeArray[1], @"^\d\d\d\d[\-/]\d\d?[\-/]\d\d? \d\d?$")) fiValueDateRangeArray[1] = DateTime.Parse($"{fiValueDateRangeArray[1]}:00:00").AddHours(1).ToString("yyyy-MM-dd HH:mm:ss"); else if (Regex.IsMatch(fiValueDateRangeArray[1], @"^\d\d\d\d[\-/]\d\d?[\-/]\d\d? \d\d?:\d\d?$")) fiValueDateRangeArray[1] = DateTime.Parse($"{fiValueDateRangeArray[1]}:00").AddMinutes(1).ToString("yyyy-MM-dd HH:mm:ss"); else if (!Regex.IsMatch(fiValueDateRangeArray[1], @"^\d\d\d\d[\-/]\d\d?[\-/]\d\d? \d\d?:\d\d?:\d\d?$")) throw new ArgumentException(CoreStrings.DateRange_DateFormat_yyyy); if (Regex.IsMatch(fiValueDateRangeArray[0], @"^\d\d\d\d[\-/]\d\d?$")) fiValueDateRangeArray[0] = DateTime.Parse($"{fiValueDateRangeArray[0]}-01").ToString("yyyy-MM-dd HH:mm:ss"); else if (Regex.IsMatch(fiValueDateRangeArray[0], @"^\d\d\d\d$")) fiValueDateRangeArray[0] = DateTime.Parse($"{fiValueDateRangeArray[0]}-01-01").ToString("yyyy-MM-dd HH:mm:ss"); else if (Regex.IsMatch(fiValueDateRangeArray[0], @"^\d\d\d\d[\-/]\d\d?[\-/]\d\d? \d\d?$")) fiValueDateRangeArray[0] = DateTime.Parse($"{fiValueDateRangeArray[0]}:00:00").ToString("yyyy-MM-dd HH:mm:ss"); else if (Regex.IsMatch(fiValueDateRangeArray[0], @"^\d\d\d\d[\-/]\d\d?[\-/]\d\d? \d\d?:\d\d?$")) fiValueDateRangeArray[0] = DateTime.Parse($"{fiValueDateRangeArray[0]}:00").ToString("yyyy-MM-dd HH:mm:ss"); exp = Expression.AndAlso( Expression.GreaterThanOrEqual(exp, Expression.Constant(Utils.GetDataReaderValue(exp.Type, fiValueDateRangeArray[0]), exp.Type)), Expression.LessThan(exp, Expression.Constant(Utils.GetDataReaderValue(exp.Type, fiValueDateRangeArray[1]), exp.Type))); break; case DynamicFilterOperator.Any: case DynamicFilterOperator.NotAny: var fiValueAnyArray = getFiListValue(); if (fiValueAnyArray.Length == 0) break; var fiValueAnyArrayType = exp.Type.MakeArrayType(); exp = Expression.Call(GetMethodEnumerable("Contains").MakeGenericMethod(exp.Type), Expression.Constant(Utils.GetDataReaderValue(fiValueAnyArrayType, fiValueAnyArray), fiValueAnyArrayType), exp); if (fi.Operator == DynamicFilterOperator.NotAny) exp = Expression.Not(exp); break; } string[] getFiListValue() { if (fi.Value is string fiValueString) return fiValueString.Split(','); if (fi.Value is IEnumerable fiValueIe) { var fiValueList = new List(); foreach (var fiValueIeItem in fiValueIe) { if (fiValueIeItem is DateTime fiValueIeItemDateTime) fiValueList.Add(fiValueIeItemDateTime.ToString("yyyy-MM-dd HH:mm:ss")); else fiValueList.Add(string.Concat(fiValueIeItem)); } return fiValueList.ToArray(); } var fiValueType = fi.Value.GetType(); if (fiValueType.FullName == "System.Text.Json.JsonElement") { var fiValueKind = fiValueType.GetProperty("ValueKind").GetValue(fi.Value, null).ToString(); if (fiValueKind == "Array") { fiValueIe = fiValueType.GetMethod("EnumerateArray", new Type[0])?.Invoke(fi.Value, null) as IEnumerable; var fiValueList = new List(); foreach (var fiValueIeItem in fiValueIe) { if (fiValueIeItem is DateTime fiValueIeItemDateTime) fiValueList.Add(fiValueIeItemDateTime.ToString("yyyy-MM-dd HH:mm:ss")); else fiValueList.Add(string.Concat(fiValueIeItem)); } return fiValueList.ToArray(); } return fi.Value.ToString().Split(','); } return new string[0]; } var sql = _commonExpression.ExpressionWhereLambda(_tables, _tableRule, exp, _diymemexpWithTempQuery, null, _params); sb.Append(sql); } if (fi.Filters?.Any() == true) { fi.Filters = fi.Filters.Where(a => IsIgnoreFilter(a) == false).ToList(); //忽略 like '%%' if (fi.Filters.Any()) { if (string.IsNullOrEmpty(fi.Field) == false) sb.Append(" AND "); if (fi.Logic == DynamicFilterLogic.Or) sb.Append("("); for (var x = 0; x < fi.Filters.Count; x++) ParseFilter(fi.Logic, fi.Filters[x], x == fi.Filters.Count - 1); if (fi.Logic == DynamicFilterLogic.Or) sb.Append(")"); } } if (isend == false) { if (string.IsNullOrEmpty(fi.Field) == false || fi.Filters?.Any() == true) { switch (logic) { case DynamicFilterLogic.And: sb.Append(" AND "); break; case DynamicFilterLogic.Or: sb.Append(" OR "); break; } } } } bool IsIgnoreFilter(DynamicFilterInfo testFilter) { return string.IsNullOrEmpty(testFilter.Field) == false && new[] { DynamicFilterOperator.Contains, DynamicFilterOperator.StartsWith, DynamicFilterOperator.EndsWith }.Contains(testFilter.Operator) && string.IsNullOrEmpty(testFilter.Value?.ToString()); } } static ConcurrentDictionary _dicMethodIsDynamicFilterCustomAttribute = new ConcurrentDictionary(); static bool MethodIsDynamicFilterCustomAttribute(MethodInfo method) => _dicMethodIsDynamicFilterCustomAttribute.GetOrAdd(method, m => { object[] attrs = null; try { attrs = m.GetCustomAttributes(false).ToArray(); //.net core 反射存在版本冲突问题,导致该方法异常 } catch { } var dyattr = attrs?.Where(a => { return ((a as Attribute)?.TypeId as Type)?.Name == "DynamicFilterCustomAttribute"; }).FirstOrDefault(); return dyattr != null; }); public TSelect DisableGlobalFilter(params string[] name) { if (_whereGlobalFilter.Any() == false) return this as TSelect; if (name?.Any() != true) { _whereGlobalFilter.Clear(); return this as TSelect; } foreach (var n in name) { if (n == null) continue; var idx = _whereGlobalFilter.FindIndex(a => string.Compare(a.Name, n, true) == 0); if (idx == -1) continue; _whereGlobalFilter.RemoveAt(idx); } return this as TSelect; } public TSelect ForUpdate(bool noawait = false) { if (_transaction == null && _orm.Ado.TransactionCurrentThread != null) this.WithTransaction(_orm.Ado.TransactionCurrentThread); if (_transaction == null && _resolveHookTransaction != null) this.WithTransaction(_resolveHookTransaction()); if (_transaction == null) throw new Exception($"{CoreStrings.Begin_Transaction_Then_ForUpdate}"); switch (_orm.Ado.DataType) { case DataType.MySql: case DataType.OdbcMySql: case DataType.CustomMySql: _tosqlAppendContent = $"{_tosqlAppendContent} for update"; break; case DataType.SqlServer: case DataType.OdbcSqlServer: case DataType.CustomSqlServer: _aliasRule = (_, old) => $"{old} With(UpdLock, RowLock{(noawait ? ", NoWait" : "")})"; break; case DataType.PostgreSQL: case DataType.OdbcPostgreSQL: case DataType.CustomPostgreSQL: case DataType.KingbaseES: case DataType.OdbcKingbaseES: _tosqlAppendContent = $"{_tosqlAppendContent} for update{(noawait ? " nowait" : "")}"; break; case DataType.Oracle: case DataType.OdbcOracle: case DataType.CustomOracle: case DataType.Dameng: case DataType.OdbcDameng: _tosqlAppendContent = $"{_tosqlAppendContent} for update{(noawait ? " nowait" : "")}"; break; case DataType.Sqlite: break; case DataType.GBase: case DataType.ShenTong: //神通测试中发现,不支持 nowait _tosqlAppendContent = $"{_tosqlAppendContent} for update"; break; case DataType.Firebird: _tosqlAppendContent = $"{_tosqlAppendContent} for update with lock"; break; } return this as TSelect; } public ISelect InternalWithTempQuery(Expression selector) { if (_orm.CodeFirst.IsAutoSyncStructure) (_orm.CodeFirst as CodeFirstProvider)._dicSycedTryAdd(typeof(TDto)); //._dicSyced.TryAdd(typeof(TReturn), true); var ret = (_orm as BaseDbProvider).CreateSelectProvider(null) as Select1Provider; ret._commandTimeout = _commandTimeout; ret._connection = _connection; ret._transaction = _transaction; ret._whereGlobalFilter = new List(_whereGlobalFilter.ToArray()); ret._cancel = _cancel; ret._params.AddRange(_params); if (ret._tables[0].Table == null) ret._tables[0].Table = TableInfo.GetDefaultTable(typeof(TDto)); if (selector is LambdaExpression lambdaExp && lambdaExp != null) { for (var a = 0; a < lambdaExp.Parameters.Count; a++) _tables[a].Parameter = lambdaExp.Parameters[a]; } var parser = new WithTempQueryParser(this, null, selector, ret._tables[0]); if (this._select.StartsWith("WITH ")) { ret._select = this._select; this._select = "SELECT "; } var sql = $"\r\n{this.ToSql(parser._insideSelectList[0].InsideField)}"; ret.WithSql(sql); ret._diymemexpWithTempQuery = parser; return ret; } public bool Any() { this.Limit(1); return this.ToList($"{1}{_commonUtils.FieldAsAlias("as1")}").Sum() > 0; //这里的 Sum 为了分表查询 } public long Count() { var tmpOrderBy = _orderby; var tmpSkip = _skip; var tmpLimit = _limit; var tmpDistinct = _distinct; _orderby = null; //解决 select count(1) from t order by id 这样的 SQL 错误 _skip = 0; _limit = 0; _distinct = false; try { var countField = "1"; if (tmpDistinct && _selectExpression != null) countField = $"distinct {this.GetExpressionField(_selectExpression, FieldAliasOptions.AsProperty).field}"; return this.ToList($"count({countField}){_commonUtils.FieldAsAlias("as1")}").Sum(); //这里的 Sum 为了分表查询 } finally { _orderby = tmpOrderBy; _skip = tmpSkip; _limit = tmpLimit; _distinct = tmpDistinct; } } public TSelect Count(out long count) { count = this.Count(); return this as TSelect; } public List ToList() => ToList(false); public virtual List ToList(bool includeNestedMembers) { if (_diymemexpWithTempQuery != null && _diymemexpWithTempQuery is WithTempQueryParser withTempQueryParser) { if (withTempQueryParser._outsideTable[0] != _tables[0]) { var tps = _tables.Select(a => { var tp = Expression.Parameter(a.Table.Type, a.Alias); a.Parameter = tp; return tp; }).ToArray(); return this.InternalToList(tps[0]); } return this.ToListMapReaderPrivate(withTempQueryParser._insideSelectList[0].InsideAf, null); } if (_selectExpression != null) return this.InternalToList(_selectExpression); return this.ToListPrivate(includeNestedMembers == false ? this.GetAllFieldExpressionTreeLevel2() : this.GetAllFieldExpressionTreeLevelAll(), null); } public T1 ToOne() { this.Limit(1); return this.ToList().FirstOrDefault(); } public T1 First() => this.ToOne(); #if net40 #else async public Task AnyAsync(CancellationToken cancellationToken = default) { this.Limit(1); return (await this.ToListAsync($"1{_commonUtils.FieldAsAlias("as1")}", cancellationToken)).Sum() > 0; //这里的 Sum 为了分表查询 } async public Task CountAsync(CancellationToken cancellationToken = default) { var tmpOrderBy = _orderby; var tmpSkip = _skip; var tmpLimit = _limit; var tmpDistinct = _distinct; _orderby = null; _skip = 0; _limit = 0; _distinct = false; try { var countField = "1"; if (tmpDistinct && _selectExpression != null) countField = $"distinct {this.GetExpressionField(_selectExpression, FieldAliasOptions.AsProperty).field}"; return (await this.ToListAsync($"count({countField}){_commonUtils.FieldAsAlias("as1")}", cancellationToken)).Sum(); //这里的 Sum 为了分表查询 } finally { _orderby = tmpOrderBy; _skip = tmpSkip; _limit = tmpLimit; _distinct = tmpDistinct; } } public Task> ToListAsync(CancellationToken cancellationToken = default) => ToListAsync(false, cancellationToken); public virtual Task> ToListAsync(bool includeNestedMembers = false, CancellationToken cancellationToken = default) { if (_diymemexpWithTempQuery != null && _diymemexpWithTempQuery is WithTempQueryParser withTempQueryParser) { if (withTempQueryParser._outsideTable[0] != _tables[0]) { var tps = _tables.Select(a => { var tp = Expression.Parameter(a.Table.Type, a.Alias); a.Parameter = tp; return tp; }).ToArray(); return this.InternalToListAsync(tps[0], cancellationToken); } return this.ToListMapReaderPrivateAsync((_diymemexpWithTempQuery as WithTempQueryParser)._insideSelectList[0].InsideAf, null, cancellationToken); } if (_selectExpression != null) return this.InternalToListAsync(_selectExpression, cancellationToken); return this.ToListPrivateAsync(includeNestedMembers == false ? this.GetAllFieldExpressionTreeLevel2() : this.GetAllFieldExpressionTreeLevelAll(), null, cancellationToken); } async public Task ToOneAsync(CancellationToken cancellationToken = default) { this.Limit(1); return (await this.ToListAsync(false, cancellationToken)).FirstOrDefault(); } public Task FirstAsync(CancellationToken cancellationToken = default) => this.ToOneAsync(cancellationToken); #endif } }