using FreeSql.Extensions.EntityUtil; using FreeSql.Internal.Model; using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Data; 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 class Select1Provider : Select0Provider, T1>, ISelect { public Select1Provider(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { _whereGlobalFilter = _orm.GlobalFilter.GetFilters(); } protected ISelect InternalFrom(LambdaExpression lambdaExp) { if (lambdaExp == null) return this; for (var a = 1; a < lambdaExp.Parameters.Count; a++) { var tb = _commonUtils.GetTableByEntity(lambdaExp.Parameters[a].Type); if (tb == null) throw new ArgumentException(CoreStrings.Type_Error_Name(lambdaExp.Parameters[a].Name)); _tables.Add(new SelectTableInfo { Table = tb, Alias = lambdaExp.Parameters[a].Name, On = null, Type = SelectTableInfoType.From }); } var exp = lambdaExp.Body; if (exp?.NodeType == ExpressionType.Call) { var expCall = exp as MethodCallExpression; var stockCall = new Stack(); while (expCall != null) { stockCall.Push(expCall); expCall = expCall.Object as MethodCallExpression; } while (stockCall.Any()) { expCall = stockCall.Pop(); switch (expCall.Method.Name) { case "Where": this.InternalWhere(expCall.Arguments[0]); break; case "WhereIf": if ((bool)Expression.Lambda(expCall.Arguments[0]).Compile().DynamicInvoke()) this.InternalWhere(expCall.Arguments[1]); break; case "OrderBy": if (expCall.Arguments.Count == 2 && expCall.Arguments[0].Type == typeof(bool)) { var ifcond = _commonExpression.ExpressionSelectColumn_MemberAccess(null, _tableRule, null, SelectTableInfoType.From, expCall.Arguments[0], false, _diymemexpWithTempQuery); if (ifcond == "1" || ifcond == "'t'") this.InternalOrderBy(expCall.Arguments.LastOrDefault()); break; } this.InternalOrderBy(expCall.Arguments.LastOrDefault()); break; case "OrderByDescending": if (expCall.Arguments.Count == 2 && expCall.Arguments[0].Type == typeof(bool)) { var ifcond = _commonExpression.ExpressionSelectColumn_MemberAccess(null, _tableRule, null, SelectTableInfoType.From, expCall.Arguments[0], false, _diymemexpWithTempQuery); if (ifcond == "1" || ifcond == "'t'" || ifcond == "-1")//MsAccess -1 this.InternalOrderByDescending(expCall.Arguments.LastOrDefault()); break; } this.InternalOrderByDescending(expCall.Arguments.LastOrDefault()); break; case "LeftJoin": this.InternalJoin(expCall.Arguments[0], SelectTableInfoType.LeftJoin); break; case "InnerJoin": this.InternalJoin(expCall.Arguments[0], SelectTableInfoType.InnerJoin); break; case "RightJoin": this.InternalJoin(expCall.Arguments[0], SelectTableInfoType.RightJoin); break; default: throw new NotImplementedException(CoreStrings.Not_Implemented_Name(expCall.Method.Name)); } } } return this; } public ISelect As(string alias) { var oldAs = _tables.First().Alias; var newAs = string.IsNullOrEmpty(alias) ? "a" : alias; if (oldAs != newAs) { _tables.First().Alias = newAs; var wh = _where.ToString(); _where.Replace($" {oldAs}.", $" {newAs}."); } return this; } public double Avg(Expression> column) { if (column == null) return default(double); _tables[0].Parameter = column.Parameters[0]; return this.InternalAvg(column?.Body); } public abstract ISelect From(Expression, T2, ISelectFromExpression>> exp = null) where T2 : class; public abstract ISelect From(Expression, T2, T3, ISelectFromExpression>> exp = null) where T2 : class where T3 : class; public abstract ISelect From(Expression, T2, T3, T4, ISelectFromExpression>> exp = null) where T2 : class where T3 : class where T4 : class; public abstract ISelect From(Expression, T2, T3, T4, T5, ISelectFromExpression>> exp = null) where T2 : class where T3 : class where T4 : class where T5 : class; public abstract ISelect From(Expression, T2, T3, T4, T5, T6, ISelectFromExpression>> exp = null) where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class; public abstract ISelect From(Expression, T2, T3, T4, T5, T6, T7, ISelectFromExpression>> exp = null) where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class; public abstract ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, ISelectFromExpression>> exp = null) where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class; public abstract ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, T9, ISelectFromExpression>> exp = null) 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 abstract ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, T9, T10, ISelectFromExpression>> exp = null) 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 abstract ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, ISelectFromExpression>> exp = null) 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 abstract ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, ISelectFromExpression>> exp = null) 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 abstract ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, ISelectFromExpression>> exp = null) 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 abstract ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, ISelectFromExpression>> exp = null) 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 abstract ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, ISelectFromExpression>> exp = null) 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 abstract ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, ISelectFromExpression>> exp = null) 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 ISelect FromQuery(ISelect select2) where T2 : class { var ret = From(); var retsp = ret as Select0Provider; var rettbs = retsp._tables; if (rettbs[1].Table == null) rettbs[1].Table = TableInfo.GetDefaultTable(typeof(T2)); (_diymemexpWithTempQuery as WithTempQueryParser)?.Append(select2, rettbs[1]); var select2sp = select2 as Select0Provider; string sql2 = null; if (select2sp._diymemexpWithTempQuery == null) { if (select2sp._tableRule == null && select2sp._tables[0].Table.Type == typeof(T2) && select2sp.IsDefaultSqlContent == true) return ret; sql2 = select2?.ToSql(a => a, FieldAliasOptions.AsProperty); } else { if (retsp._diymemexpWithTempQuery == null) retsp._diymemexpWithTempQuery = new WithTempQueryParser(null, null, null, null).Append(select2, rettbs[1]); if (select2sp._tableRule != null && select2sp.IsDefaultSqlContent == true) { sql2 = select2sp._tableRule(select2sp._tables[0].Table.Type, null); if (sql2.StartsWith("(") && sql2.EndsWith(")")) sql2 = sql2.Substring(1, sql2.Length - 2); if (sql2.StartsWith(" \r\n")) sql2 = sql2.Substring(3); } if (string.IsNullOrWhiteSpace(sql2)) sql2 = select2?.ToSql("*"); } if (retsp._tableRules.Count > 0) { var tbrules = retsp._tableRules.ToList(); retsp._tableRules.Clear(); tbrules.ForEach(tbrule => { var tbruler1 = tbrule(typeof(T1), retsp._tables[0].Table.DbName); if (string.IsNullOrWhiteSpace(tbruler1) == false) retsp._tableRules.Add((type, old) => { if (type == typeof(T1)) return tbruler1; if (type == typeof(T2)) return $"( \r\n{sql2})"; return old; }); }); } if (retsp._tableRules.Count == 0) ret.WithSql(null, $" \r\n{sql2}"); return ret; } public ISelect UnionAll(params ISelect[] querys) { var sql1 = this.ToSql(); var ret = (_orm as BaseDbProvider).CreateSelectProvider(null) as Select1Provider; var sb = new StringBuilder().Append(this.ToSql()); foreach (var select2 in querys) sb.Append(" \r\nUNION ALL \r\n").Append(select2.ToSql()); ret.WithSql(sb.ToString()); sb.Clear(); ret._commandTimeout = _commandTimeout; ret._connection = _connection; ret._transaction = _transaction; ret._whereGlobalFilter = new List(_whereGlobalFilter.ToArray()); ret._cancel = _cancel; ret._diymemexpWithTempQuery = _diymemexpWithTempQuery; ret._tables[0] = _tables[0]; ret._params = _params; return ret; } public ISelectGrouping GroupBy(Expression> columns) { if (columns == null) return this.InternalGroupBy(columns); _tables[0].Parameter = columns.Parameters[0]; return this.InternalGroupBy(columns); } public TMember Max(Expression> column) { if (column == null) return default(TMember); _tables[0].Parameter = column.Parameters[0]; return this.InternalMax(column.Body); } public TMember Min(Expression> column) { if (column == null) return default(TMember); _tables[0].Parameter = column.Parameters[0]; return this.InternalMin(column.Body); } public void OrderByReflection(LambdaExpression column, bool isDescending) { if (column == null) return; _tables[0].Parameter = column.Parameters[0]; if (isDescending) this.InternalOrderByDescending(column.Body); else this.InternalOrderBy(column.Body); } public ISelect OrderBy(Expression> column) => this.OrderBy(true, column); public ISelect OrderBy(bool condition, Expression> column) { if (condition == false || column == null) return this; _tables[0].Parameter = column.Parameters[0]; return this.InternalOrderBy(column.Body); } public ISelect OrderByDescending(Expression> column) => this.OrderByDescending(true, column); public ISelect OrderByDescending(bool condition, Expression> column) { if (condition == false || column == null) return this; _tables[0].Parameter = column.Parameters[0]; return this.InternalOrderByDescending(column.Body); } public ISelect OrderByIf(bool condition, Expression> column, bool descending = false) => descending ? this.OrderByDescending(condition, column) : this.OrderBy(condition, column); public decimal Sum(Expression> column) { if (column == null) return default(decimal); _tables[0].Parameter = column.Parameters[0]; return this.InternalSum(column.Body); } class IncludeManyNewInit { public TableInfo Table { get; } public Dictionary Childs { get; } = new Dictionary(); public Expression CurrentExpression { get; } public bool IsOutputPrimary { get; set; } public IncludeManyNewInit(TableInfo table, Expression currentExpression) { this.Table = table; this.CurrentExpression = currentExpression; } } public List ToList(Expression> select) { if (select == null) return this.InternalToList(select?.Body); _tables[0].Parameter = select.Parameters[0]; if (_includeToList?.Any() != true) return this.InternalToList(select.Body); var findIncludeMany = new List(); //支持指定已经使用 IncudeMany 的导航属性 var map = new ReadAnonymousTypeInfo(); var field = new StringBuilder(); var index = 0; _commonExpression.ReadAnonymousField(_tables, _tableRule, field, map, ref index, select.Body, this, _diymemexpWithTempQuery, _whereGlobalFilter, findIncludeMany, null, true); var af = new ReadAnonymousTypeAfInfo(map, field.Length > 0 ? field.Remove(0, 2).ToString() : null); if (findIncludeMany.Any() == false) return this.ToListMapReaderPrivate(af, null); var parmExp = Expression.Parameter(_tables[0].Table.Type, _tables[0].Alias); var incNewInit = new IncludeManyNewInit(_tables[0].Table, parmExp); foreach (var inc in _includeInfo) { var curIncNewInit = incNewInit; Expression curParmExp = parmExp; for (var a = 0; a < inc.Value.Length - 1; a++) { curParmExp = Expression.MakeMemberAccess(parmExp, inc.Value[a].Member); if (curIncNewInit.Childs.ContainsKey(inc.Value[a].Member.Name) == false) curIncNewInit.Childs.Add(inc.Value[a].Member.Name, curIncNewInit = new IncludeManyNewInit(_orm.CodeFirst.GetTableByEntity(inc.Value[a].Type), curParmExp)); else curIncNewInit = curIncNewInit.Childs[inc.Value[a].Member.Name]; } curIncNewInit.IsOutputPrimary = true; } MemberInitExpression GetIncludeManyNewInitExpression(IncludeManyNewInit imni) { var bindings = new List(); if (imni.IsOutputPrimary) bindings.AddRange(imni.Table.Primarys.Select(a => Expression.Bind(imni.Table.Properties[a.CsName], Expression.MakeMemberAccess(imni.CurrentExpression, imni.Table.Properties[a.CsName])))); if (imni.Childs.Any()) bindings.AddRange(imni.Childs.Select(a => Expression.Bind(imni.Table.Properties[a.Key], GetIncludeManyNewInitExpression(a.Value)))); var pgarrayToManys = imni.Table.GetAllTableRef().Select(tr => { if (tr.Value.RefType != TableRefType.PgArrayToMany) return null; var reftb = _orm.CodeFirst.GetTableByEntity(tr.Value.RefEntityType); if (tr.Value.RefColumns[0] == reftb.Primarys[0]) { bindings.Add(Expression.Bind(imni.Table.Properties[tr.Value.Columns[0].CsName], Expression.MakeMemberAccess(imni.CurrentExpression, imni.Table.Properties[tr.Value.Columns[0].CsName]))); return tr.Key; } return null; }).ToList(); return Expression.MemberInit(imni.Table.Type.InternalNewExpression(), bindings); } var otherNewInit = GetIncludeManyNewInitExpression(incNewInit); //获取 IncludeMany 包含的最简化字段 if (otherNewInit.Bindings.Any() == false) return this.ToListMapReaderPrivate(af, null); var otherMap = new ReadAnonymousTypeInfo(); field.Clear(); _commonExpression.ReadAnonymousField(_tables, _tableRule, field, otherMap, ref index, otherNewInit, this, _diymemexpWithTempQuery, _whereGlobalFilter, null, null, true); var otherRet = new List(); var otherAf = new ReadAnonymousTypeOtherInfo(field.ToString(), otherMap, otherRet); af.fillIncludeMany = new List>(); var ret = this.ToListMapReaderPrivate(af, new[] { otherAf }); this.SetList(otherRet.Select(a => (T1)a).ToList()); //级联加载 foreach (var fim in af.fillIncludeMany) { var splitKeys = fim.Item1.Split('.'); var otherRetItem = otherRet[fim.Item3]; var otherRetItemType = _tables[0].Table.Type; foreach (var splitKey in splitKeys) { otherRetItem = _orm.GetEntityValueWithPropertyName(otherRetItemType, otherRetItem, splitKey); otherRetItemType = _orm.CodeFirst.GetTableByEntity(otherRetItemType).Properties[splitKey].PropertyType; } if (otherRetItem == null) continue; var otherList = otherRetItem as IEnumerable; foreach (var otherListItem in otherList) fim.Item2.Add(otherListItem); } return ret; } public List ToList() => typeof(T1) == typeof(TDto) ? ToList() as List : ToList(GetToListDtoSelector()); Expression> GetToListDtoSelector() { var expParam = _tables[0].Parameter ?? Expression.Parameter(typeof(T1), "a"); var expBinds = _tables[0].Table.Columns .Where(a => a.Value.CsType != a.Value.Attribute.MapType) .Select(a => new { DtoProperty = typeof(TDto).GetProperty(a.Value.CsName), EntityProperty = _tables[0].Table.Properties[a.Value.CsName], Column = a.Value }) .Where(a => a.DtoProperty != null) .Select(a => a.DtoProperty.PropertyType == a.EntityProperty.PropertyType ? Expression.Bind(a.DtoProperty, Expression.MakeMemberAccess(expParam, a.EntityProperty)) : Expression.Bind(a.DtoProperty, Expression.Convert(Expression.MakeMemberAccess(expParam, a.EntityProperty), a.DtoProperty.PropertyType)) ) .ToArray(); return Expression.Lambda>( Expression.MemberInit(typeof(TDto).InternalNewExpression(), expBinds), expParam); } public void ToChunk(Expression> select, int size, Action>> done) { if (select == null || done == null) return; _tables[0].Parameter = select.Parameters[0]; this.InternalToChunk(select.Body, size, done); } public DataTable ToDataTable(Expression> select) { if (select == null) return this.InternalToDataTable(select?.Body); _tables[0].Parameter = select.Parameters[0]; return this.InternalToDataTable(select?.Body); } public string ToSql(Expression> select, FieldAliasOptions fieldAlias = FieldAliasOptions.AsIndex) { if (select == null) return this.InternalToSql(select?.Body, fieldAlias); _tables[0].Parameter = select.Parameters[0]; return this.InternalToSql(select?.Body, fieldAlias); } public TReturn ToAggregate(Expression, TReturn>> select) { if (select == null) return default(TReturn); _tables[0].Parameter = select.Parameters[0]; return this.InternalToAggregate(select?.Body); } public ISelect Aggregate(Expression, TReturn>> select, out TReturn result) { result = ToAggregate(select); return this; } public ISelect Where(Expression> exp) => WhereIf(true, exp); public ISelect WhereIf(bool condition, Expression> exp) { if (condition == false || exp == null) return this; _tables[0].Parameter = exp.Parameters[0]; return this.InternalWhere(exp?.Body); } public ISelect Where(Expression> exp) where T2 : class { if (exp == null) return this; _tables[0].Parameter = exp.Parameters[0]; return this.InternalWhere(exp?.Body); } public ISelect Where(Expression> exp) where T2 : class { if (exp == null) return this; //_tables[0].Parameter = exp.Parameters[0]; return this.InternalWhere(exp?.Body); } public ISelect Where(Expression> exp) where T2 : class where T3 : class { if (exp == null) return this; _tables[0].Parameter = exp.Parameters[0]; return this.InternalWhere(exp?.Body); } public ISelect Where(Expression> exp) where T2 : class where T3 : class where T4 : class { if (exp == null) return this; _tables[0].Parameter = exp.Parameters[0]; return this.InternalWhere(exp?.Body); } public ISelect Where(Expression> exp) where T2 : class where T3 : class where T4 : class where T5 : class { if (exp == null) return this; _tables[0].Parameter = exp.Parameters[0]; return this.InternalWhere(exp?.Body); } public ISelect WhereDynamic(object dywhere, bool not = false) { if (dywhere is DynamicFilterInfo dyfilter) { if (not == false) return this.WhereDynamicFilter(dyfilter); var oldwhere = _where.ToString(); _where.Clear(); this.WhereDynamicFilter(dyfilter); var newwhere = _where.ToString(); _where.Clear(); return this .Where(oldwhere) .WhereIf(string.IsNullOrWhiteSpace(newwhere) == false, $"not({newwhere})"); } var wheresql = _commonUtils.WhereObject(_tables.First().Table, $"{_tables.First().Alias}.", dywhere); return not == false ? this.Where(wheresql) : this.Where($"not({wheresql})"); } public ISelect WhereCascade(Expression> exp) { if (exp != null) _whereGlobalFilter.Add(new GlobalFilter.Item { Name = "WhereCascade", Only = false, Where = exp }); return this; } public ISelect WithSql(string sql, object parms = null) { this.AsTable((type, old) => { if (type == _tables[0].Table?.Type && string.IsNullOrEmpty(sql) == false) return $"( {sql} )"; return old; }); if (parms != null) _params.AddRange(_commonUtils.GetDbParamtersByObject(sql, parms)); return this; } public ISelect WithMemory(IEnumerable source) { var sb = new StringBuilder(); (_orm.InsertOrUpdate().AsType(_tables[0].Table.Type) as InsertOrUpdateProvider) .WriteSourceSelectUnionAll(source.Select(a => (object)a).ToList(), sb, _params); try { return WithSql(sb.ToString()); } finally { sb.Clear(); } } public ISelect WithTempQuery(Expression> selector) => InternalWithTempQuery(selector); public bool Any(Expression> exp) { var oldwhere = _where.ToString(); var ret = this.Where(exp).Any(); _where.Clear().Append(oldwhere); return ret; } public TReturn ToOne(Expression> select) => this.Limit(1).ToList(select).FirstOrDefault(); public TDto ToOne() => this.Limit(1).ToList().FirstOrDefault(); public TReturn First(Expression> select) => this.ToOne(select); public TDto First() => this.ToOne(); public override List ToList(bool includeNestedMembers = false) => base.ToList(_isIncluded || includeNestedMembers); public int InsertInto(string tableName, Expression> select) where TTargetEntity : class => base.InternalInsertInto(tableName, select); public ISelect IncludeByPropertyNameIf(bool condition, string property) => condition ? IncludeByPropertyName(property, null) : this; public ISelect IncludeByPropertyNameIf(bool condition, string property, Expression>> then) => condition ? IncludeByPropertyName(property, then) : this; public ISelect IncludeByPropertyName(string property) => IncludeByPropertyName(property, null); public ISelect IncludeByPropertyName(string property, Expression>> then) { var exp = ConvertStringPropertyToExpression(property, true); if (exp == null) throw new ArgumentException($"{CoreStrings.Cannot_Resolve_ExpressionTree(nameof(property))}"); var memExp = exp as MemberExpression; if (memExp == null) throw new ArgumentException($"{CoreStrings.Cannot_Resolve_ExpressionTree(nameof(property))}2"); var parTb = _commonUtils.GetTableByEntity(memExp.Expression.Type); if (parTb == null) throw new ArgumentException($"{CoreStrings.Cannot_Resolve_ExpressionTree(nameof(property))}3"); var parTbref = parTb.GetTableRef(memExp.Member.Name, true); if (parTbref == null) throw new ArgumentException(CoreStrings.Not_Valid_Navigation_Property(nameof(property))); switch (parTbref.RefType) { case TableRefType.ManyToMany: case TableRefType.OneToMany: case TableRefType.PgArrayToMany: var funcType = typeof(Func<,>).MakeGenericType(typeof(T1), typeof(IEnumerable<>).MakeGenericType(parTbref.RefEntityType)); if (_tables[0].Table.Type != typeof(T1)) { var expParm = Expression.Parameter(typeof(T1), _tables[0].Alias); exp = new ReplaceMemberExpressionVisitor().Replace(exp, _tables[0].Parameter, Expression.Convert(expParm, _tables[0].Table.Type)); _tables[0].Parameter = expParm; } var navigateSelector = Expression.Lambda(funcType, exp, _tables[0].Parameter); var incMethod = this.GetType().GetMethod("IncludeMany"); if (incMethod == null) throw new Exception(CoreStrings.RunTimeError_Reflection_IncludeMany); Delegate newthen = null; if (then != null) { var newthenParm = Expression.Parameter(typeof(ISelect<>).MakeGenericType(parTbref.RefEntityType)); var newthenLambdaBody = new ReplaceIncludeByPropertyNameParameterVisitor().Modify(then, newthenParm); var newthenLambda = Expression.Lambda(typeof(Action<>).MakeGenericType(newthenParm.Type), newthenLambdaBody, newthenParm); newthen = newthenLambda.Compile(); } incMethod.MakeGenericMethod(parTbref.RefEntityType).Invoke(this, new object[] { navigateSelector, newthen }); break; case TableRefType.ManyToOne: case TableRefType.OneToOne: _isIncluded = true; var curTb = _commonUtils.GetTableByEntity(exp.Type); _commonExpression.ExpressionWhereLambda(_tables, _tableRule, Expression.MakeMemberAccess(exp, curTb.Properties[curTb.ColumnsByCs.First().Value.CsName]), _diymemexpWithTempQuery, null, null); break; } return this; } public class ReplaceIncludeByPropertyNameParameterVisitor : ExpressionVisitor { private Expression _replaceExp; private ParameterExpression oldParameter; public Expression Modify(LambdaExpression lambda, Expression replaceExp) { this._replaceExp = replaceExp; this.oldParameter = lambda.Parameters.FirstOrDefault(); return Visit(lambda.Body); } protected override Expression VisitMember(MemberExpression node) { if (node.Expression?.NodeType == ExpressionType.Parameter && node.Expression == oldParameter) return Expression.Property(_replaceExp, node.Member.Name); return base.VisitMember(node); } protected override Expression VisitMethodCall(MethodCallExpression node) { if (node.Object?.Type == oldParameter.Type) { var methodParameterTypes = node.Method.GetParameters().Select(a => a.ParameterType).ToArray(); var method = _replaceExp.Type.GetMethod(node.Method.Name, methodParameterTypes); if (node.Object?.NodeType == ExpressionType.Parameter && node.Object == oldParameter) return Expression.Call(_replaceExp, method, node.Arguments); return Expression.Call(Visit(node.Object), method, node.Arguments); } return base.VisitMethodCall(node); } } bool _isIncluded = false; public ISelect IncludeIf(bool condition, Expression> navigateSelector) where TNavigate : class => condition ? Include(navigateSelector) : this; public ISelect Include(Expression> navigateSelector) where TNavigate : class { var expBody = navigateSelector?.Body; if (expBody == null) return this; if (expBody.NodeType != ExpressionType.MemberAccess) throw new Exception(CoreStrings.Include_ParameterType_Error_Use_MemberAccess); if (typeof(IEnumerable).IsAssignableFrom(expBody.Type)) throw new Exception(CoreStrings.Include_ParameterType_Error_Use_IncludeMany); var tb = _commonUtils.GetTableByEntity(expBody.Type); if (tb == null) throw new Exception(CoreStrings.Include_ParameterType_Error); _isIncluded = true; _tables[0].Parameter = navigateSelector.Parameters[0]; _commonExpression.ExpressionWhereLambda(_tables, _tableRule, Expression.MakeMemberAccess(expBody, tb.Properties[tb.ColumnsByCs.First().Value.CsName]), _diymemexpWithTempQuery, null, null); return this; } static NativeTuple> GetExpressionStack(Expression exp) { Expression tmpExp = exp; ParameterExpression param = null; var members = new Stack(); var isbreak = false; while (isbreak == false) { switch (tmpExp.NodeType) { case ExpressionType.MemberAccess: var memExp = tmpExp as MemberExpression; tmpExp = memExp.Expression; members.Push(memExp); continue; case ExpressionType.Parameter: param = tmpExp as ParameterExpression; isbreak = true; break; case ExpressionType.Convert: var convertExp = tmpExp as UnaryExpression; if (convertExp?.Operand.NodeType == ExpressionType.Parameter) { param = convertExp.Operand as ParameterExpression; isbreak = true; break; } throw new Exception(CoreStrings.Expression_Error_Use_Successive_MemberAccess_Type(exp)); default: throw new Exception(CoreStrings.Expression_Error_Use_Successive_MemberAccess_Type(exp)); } } if (param == null) throw new Exception(CoreStrings.Expression_Error_Use_ParameterExpression(exp)); return NativeTuple.Create(param, members.ToList()); } static MethodInfo GetEntityValueWithPropertyNameMethod = typeof(EntityUtilExtensions).GetMethod("GetEntityValueWithPropertyName"); static ConcurrentDictionary> _dicTypeMethod = new ConcurrentDictionary>(); public ISelect IncludeMany(Expression>> navigateSelector, Action> then = null) where TNavigate : class { var throwNavigateSelector = new Exception(CoreStrings.IncludeMany_ParameterType_Error_Use_MemberAccess); var expBody = navigateSelector?.Body; if (expBody == null) return this; if (expBody.NodeType == ExpressionType.Convert) expBody = (expBody as UnaryExpression)?.Operand; //- 兼容 Vb.Net 无法使用 IncludeMany 的问题; MethodCallExpression whereExp = null; int takeNumber = 0; Expression> selectExp = null; while (expBody.NodeType == ExpressionType.Call) { throwNavigateSelector = new Exception(CoreStrings.IncludeMany_ParameterTypeError(nameof(navigateSelector))); var callExp = (expBody as MethodCallExpression); switch (callExp.Method.Name) { case "Where": whereExp = callExp; break; case "Take": takeNumber = int.Parse(_commonExpression.ExpressionLambdaToSql(callExp.Arguments[1], new CommonExpression.ExpTSC { })); break; case "Select": selectExp = (callExp.Arguments[1] as Expression>); if (selectExp?.Parameters.Count != 1) throw new Exception(CoreStrings.IncludeMany_ParameterError_OnlyUseOneParameter(nameof(navigateSelector))); break; default: throw throwNavigateSelector; } expBody = callExp.Object ?? callExp.Arguments.FirstOrDefault(); } if (expBody.NodeType != ExpressionType.MemberAccess) throw throwNavigateSelector; var collMem = expBody as MemberExpression; var ges = GetExpressionStack(collMem.Expression); var membersParam = ges.Item1; var members = ges.Item2; var tb = _commonUtils.GetTableByEntity(collMem.Expression.Type); if (tb == null) throw throwNavigateSelector; var collMemElementType = (collMem.Type.IsGenericType ? collMem.Type.GetGenericArguments().FirstOrDefault() : collMem.Type.GetElementType()); if (typeof(TNavigate) != collMemElementType) throw new Exception(CoreStrings.IncludeMany_ParameterError_Select_ReturnConsistentType(nameof(navigateSelector), collMemElementType)); var tbNav = _commonUtils.GetTableByEntity(typeof(TNavigate)); if (tbNav == null) throw new Exception(CoreStrings.TypeError_CannotUse_IncludeMany(typeof(TNavigate).FullName)); if (collMem.Expression.NodeType != ExpressionType.Parameter) _commonExpression.ExpressionWhereLambda(_tables, _tableRule, Expression.MakeMemberAccess(collMem.Expression, tb.Properties[tb.ColumnsByCs.First().Value.CsName]), _diymemexpWithTempQuery, null, null); TableRef tbref = null; var tbrefOneToManyColumns = new List>(); //临时 OneToMany 三个表关联,第三个表需要前两个表确定 if (whereExp == null) { tbref = tb.GetTableRef(collMem.Member.Name, true); if (tbref == null) throw new Exception(CoreStrings.IncludeMany_NotValid_Navigation(tb.Type.DisplayCsharp(), collMem.Member.Name)); } else { //处理临时关系映射 tbref = new TableRef { RefType = TableRefType.OneToMany, Property = tb.Properties[collMem.Member.Name], RefEntityType = tbNav.Type }; foreach (var whereExpArg in whereExp.Arguments) { if (whereExpArg.NodeType != ExpressionType.Lambda) continue; var whereExpArgLamb = whereExpArg as LambdaExpression; Action actWeiParse = null; actWeiParse = expOrg => { var binaryExp = expOrg as BinaryExpression; if (binaryExp == null) throw throwNavigateSelector; switch (binaryExp.NodeType) { case ExpressionType.AndAlso: actWeiParse(binaryExp.Left); actWeiParse(binaryExp.Right); break; case ExpressionType.Equal: Expression leftExp = binaryExp.Left; Expression rightExp = binaryExp.Right; while (leftExp.NodeType == ExpressionType.Convert) leftExp = (leftExp as UnaryExpression)?.Operand; while (rightExp.NodeType == ExpressionType.Convert) rightExp = (rightExp as UnaryExpression)?.Operand; var leftP1MemberExp = leftExp as MemberExpression; var rightP1MemberExp = rightExp as MemberExpression; if (leftP1MemberExp == null || rightP1MemberExp == null) throw throwNavigateSelector; if (leftP1MemberExp.Expression == whereExpArgLamb.Parameters[0]) { var rightGes = GetExpressionStack(rightP1MemberExp.Expression); var rightMembersParam = rightGes.Item1; var rightMembers = rightGes.Item2; if (rightMembersParam != membersParam) throw throwNavigateSelector; var isCollMemEquals = rightMembers.Count == members.Count; if (isCollMemEquals) { for (var l = 0; l < members.Count; l++) if (members[l].Member != rightMembers[l].Member) { isCollMemEquals = false; break; } } if (isCollMemEquals) { tbref.Columns.Add(tb.ColumnsByCs[rightP1MemberExp.Member.Name]); tbrefOneToManyColumns.Add(null); } else { var tmpTb = _commonUtils.GetTableByEntity(rightP1MemberExp.Expression.Type); if (tmpTb == null) throw throwNavigateSelector; tbref.Columns.Add(tmpTb.ColumnsByCs[rightP1MemberExp.Member.Name]); tbrefOneToManyColumns.Add(rightMembers); } tbref.RefColumns.Add(tbNav.ColumnsByCs[leftP1MemberExp.Member.Name]); return; } if (rightP1MemberExp.Expression == whereExpArgLamb.Parameters[0]) { var leftGes = GetExpressionStack(leftP1MemberExp.Expression); var leftMembersParam = leftGes.Item1; var leftMembers = leftGes.Item2; if (leftMembersParam != membersParam) throw throwNavigateSelector; var isCollMemEquals = leftMembers.Count == members.Count; if (isCollMemEquals) { for (var l = 0; l < members.Count; l++) if (members[l].Member != leftMembers[l].Member) { isCollMemEquals = false; break; } } if (isCollMemEquals) { tbref.Columns.Add(tb.ColumnsByCs[leftP1MemberExp.Member.Name]); tbrefOneToManyColumns.Add(null); } else { var tmpTb = _commonUtils.GetTableByEntity(leftP1MemberExp.Expression.Type); if (tmpTb == null) throw throwNavigateSelector; tbref.Columns.Add(tmpTb.ColumnsByCs[leftP1MemberExp.Member.Name]); tbrefOneToManyColumns.Add(leftMembers); } tbref.RefColumns.Add(tbNav.ColumnsByCs[rightP1MemberExp.Member.Name]); return; } throw throwNavigateSelector; default: throw throwNavigateSelector; } }; actWeiParse(whereExpArgLamb.Body); break; } if (tbref.Columns.Any() == false) throw throwNavigateSelector; } if (members.Count >= 2) _isIncluded = true; #if net40 Action includeToListSyncOrAsync = (listObj, isAsync) => { isAsync = false; #else Func includeToListSyncOrAsync = async (listObj, isAsync, cancellationToken) => { #endif var list = listObj as List; if (list == null) return; if (list.Any() == false) return; if (tbref.Columns.Any() == false) return; var t1parm = Expression.Parameter(typeof(T1)); Expression membersExp = t1parm; Expression membersExpNotNull = null; if (typeof(T1) != _tables[0].Table.Type) membersExp = Expression.TypeAs(membersExp, _tables[0].Table.Type); foreach (var mem in members) { membersExp = Expression.MakeMemberAccess(membersExp, mem.Member); var expNotNull = Expression.NotEqual(membersExp, Expression.Constant(null)); if (membersExpNotNull == null) membersExpNotNull = expNotNull; else membersExpNotNull = Expression.AndAlso(membersExpNotNull, expNotNull); } //members.Clear(); 此行影响 ToChunk 第二次 var isObservableCollection = collMem.Type == typeof(ObservableCollection); var listValueExp = Expression.Parameter(typeof(List), "listValue"); var setListValue = membersExpNotNull == null ? Expression.Lambda>>( isObservableCollection ? (Expression)Expression.IfThen( Expression.NotEqual(listValueExp, Expression.Constant(null, typeof(List))), Expression.Assign(Expression.MakeMemberAccess(membersExp, collMem.Member), Expression.New(typeof(ObservableCollection).GetConstructor(new[] { typeof(List) }), listValueExp)) ) : Expression.Assign(Expression.MakeMemberAccess(membersExp, collMem.Member), Expression.TypeAs(listValueExp, collMem.Type)) , t1parm, listValueExp).Compile() : Expression.Lambda>>(Expression.IfThen(membersExpNotNull, isObservableCollection ? (Expression)Expression.IfThen( Expression.NotEqual(listValueExp, Expression.Constant(null, typeof(List))), Expression.Assign(Expression.MakeMemberAccess(membersExp, collMem.Member), Expression.New(typeof(ObservableCollection).GetConstructor(new[] { typeof(List) }), listValueExp)) ) : Expression.Assign(Expression.MakeMemberAccess(membersExp, collMem.Member), Expression.TypeAs(listValueExp, collMem.Type)) ), t1parm, listValueExp).Compile(); var returnTarget = Expression.Label(typeof(object)); var propertyNameExp = Expression.Parameter(typeof(string), "propertyName"); var getListValue1 = membersExpNotNull == null ? Expression.Lambda>( Expression.Block( Expression.IfThenElse( Expression.Equal(propertyNameExp, Expression.Constant("")), //propertyName == "" 返回自身 Expression.Return(returnTarget, membersExp), Expression.Return(returnTarget, Expression.Call(null, GetEntityValueWithPropertyNameMethod, Expression.Constant(_orm), Expression.Constant(membersExp.Type), membersExp, propertyNameExp)) ), Expression.Label(returnTarget, Expression.Default(typeof(object))) ), t1parm, propertyNameExp).Compile() : Expression.Lambda>( Expression.Block( Expression.IfThen( membersExpNotNull, Expression.IfThenElse( Expression.Equal(propertyNameExp, Expression.Constant("")), Expression.Return(returnTarget, membersExp), Expression.Return(returnTarget, Expression.Call(null, GetEntityValueWithPropertyNameMethod, Expression.Constant(_orm), Expression.Constant(membersExp.Type), membersExp, propertyNameExp)) ) ), Expression.Label(returnTarget, Expression.Default(typeof(object))) ), t1parm, propertyNameExp).Compile(); var getListValue2 = new List>(); for (var j = 0; j < tbrefOneToManyColumns.Count; j++) { if (tbrefOneToManyColumns[j] == null) { getListValue2.Add(null); continue; } Expression tbrefOneToManyColumnsMembers = t1parm; Expression tbrefOneToManyColumnsMembersNotNull = null; foreach (var mem in tbrefOneToManyColumns[j]) { tbrefOneToManyColumnsMembers = Expression.MakeMemberAccess(tbrefOneToManyColumnsMembers, mem.Member); var expNotNull = Expression.NotEqual(membersExp, Expression.Constant(null)); if (tbrefOneToManyColumnsMembersNotNull == null) tbrefOneToManyColumnsMembersNotNull = expNotNull; else tbrefOneToManyColumnsMembersNotNull = Expression.AndAlso(tbrefOneToManyColumnsMembersNotNull, expNotNull); } tbrefOneToManyColumns[j].Clear(); getListValue2.Add(tbrefOneToManyColumnsMembersNotNull == null ? Expression.Lambda>( Expression.Block( Expression.Return(returnTarget, Expression.Call(null, GetEntityValueWithPropertyNameMethod, Expression.Constant(_orm), Expression.Constant(tbrefOneToManyColumnsMembers.Type), tbrefOneToManyColumnsMembers, propertyNameExp)), Expression.Label(returnTarget, Expression.Default(typeof(object))) ), t1parm, propertyNameExp).Compile() : Expression.Lambda>( Expression.Block( Expression.IfThen( tbrefOneToManyColumnsMembersNotNull, Expression.Return(returnTarget, Expression.Call(null, GetEntityValueWithPropertyNameMethod, Expression.Constant(_orm), Expression.Constant(tbrefOneToManyColumnsMembers.Type), tbrefOneToManyColumnsMembers, propertyNameExp)) ), Expression.Label(returnTarget, Expression.Default(typeof(object))) ), t1parm, propertyNameExp).Compile()); } tbrefOneToManyColumns.Clear(); Func getListValue = (item, propName, colIndex) => { if (getListValue2.Any() && getListValue2[colIndex] != null) return getListValue2[colIndex](item, propName); return getListValue1(item, propName); }; if (list.Where(a => getListValue1(a, "") == null).Count() == list.Count) return; //Parent.Childs 当 parent 都是 NULL 就没有和要向下查询了 foreach (var item in list) setListValue(item, null); Action, TableInfo> fillOneToManyData = (subList, tbref2) => { if (subList.Any() == false) { foreach (var item in list) setListValue(item, new List()); return; } Dictionary>>> dicList = new Dictionary>>>(); foreach (var item in list) { if (tbref.Columns.Count == 1) { var dicListKey = getListValue(item, tbref.Columns[0].CsName, 0)?.ToString(); if (dicListKey == null) continue; var dicListVal = Tuple.Create(item, new List()); if (dicList.TryGetValue(dicListKey, out var items) == false) dicList.Add(dicListKey, items = new List>>()); items.Add(dicListVal); } else { var sb = new StringBuilder(); for (var z = 0; z < tbref.Columns.Count; z++) { if (z > 0) sb.Append("*$*"); sb.Append(getListValue(item, tbref.Columns[z].CsName, z)); } var dicListKey = sb.ToString(); var dicListVal = Tuple.Create(item, new List()); if (dicList.TryGetValue(dicListKey, out var items) == false) dicList.Add(dicListKey, items = new List>>()); items.Add(dicListVal); sb.Clear(); } } var parentNavs = new List(); foreach (var tr2 in tbref2.GetAllTableRef()) { var tr2ref = tr2.Value; if (tr2ref.Exception != null) continue; if (tbref2.ColumnsByCs.ContainsKey(tr2.Key)) continue; if (tbref2.ColumnsByCsIgnore.ContainsKey(tr2.Key)) continue; if (tr2ref.RefType != TableRefType.ManyToOne) continue; if (tr2ref.RefEntityType != tb.Type) continue; if (string.Join(",", tr2ref.Columns.Select(a => a.CsName).OrderBy(a => a)) != string.Join(",", tbref.RefColumns.Select(a => a.CsName).OrderBy(a => a))) continue; //- 修复 IncludeMany 只填充子属性中双向关系的 ManyToOne 对象值;防止把 ManyToOne 多个相同类型的导航属性值都填充了 parentNavs.Add(tr2.Key); } foreach (var nav in subList) { string key = null; if (tbref.RefColumns.Count == 1) { key = _orm.GetEntityValueWithPropertyName(tbref.RefEntityType, nav, tbref.RefColumns[0].CsName)?.ToString() ?? ""; } else { var sb = new StringBuilder(); for (var z = 0; z < tbref.RefColumns.Count; z++) { if (z > 0) sb.Append("*$*"); sb.Append(_orm.GetEntityValueWithPropertyName(tbref.RefEntityType, nav, tbref.RefColumns[z].CsName)); } key = sb.ToString(); sb.Clear(); } if (dicList.TryGetValue(key, out var t1items) == false) continue; foreach (var t1item in t1items) t1item.Item2.Add(nav); //将子集合的,多对一,对象设置为当前对象 foreach (var parentNav in parentNavs) foreach (var t1item in t1items) _orm.SetEntityValueWithPropertyName(tbref.RefEntityType, nav, parentNav, getListValue1(t1item.Item1, "")); //propertyName == "" 返回自身 } foreach (var t1items in dicList.Values) foreach (var t1item in t1items) setListValue(t1item.Item1, t1item.Item2); dicList.Clear(); }; if (tbref.RefType == TableRefType.OneToMany && _includeManySubListOneToManyTempValue1 != null && _includeManySubListOneToManyTempValue1 is List) { fillOneToManyData(_includeManySubListOneToManyTempValue1 as List, _commonUtils.GetTableByEntity(tbref.RefEntityType)); return; } var subSelect = _orm.Select() .DisableGlobalFilter() .WithConnection(_connection) .WithTransaction(_transaction) .TrackToList(_trackToList) as Select1Provider; if (_tableRules?.Any() == true) foreach (var tr in _tableRules) subSelect.AsTable(tr); if (_whereGlobalFilter.Any()) subSelect._whereGlobalFilter.AddRange(_whereGlobalFilter.ToArray()); //subSelect._aliasRule = _aliasRule; //把 SqlServer 查询锁传递下去 then?.Invoke(subSelect); var subSelectT1Alias = subSelect._tables[0].Alias; var oldWhere = subSelect._where.ToString(); if (oldWhere.StartsWith(" AND ")) oldWhere = oldWhere.Remove(0, 5); if (selectExp != null) { var tmpinitExp = selectExp.Body as MemberInitExpression; var newinitExpBindings = tmpinitExp.Bindings.ToList(); foreach (var tbrefCol in tbref.RefColumns) { if (newinitExpBindings.Any(a => a.Member.Name == tbrefCol.CsName)) continue; var tmpMemberInfo = tbrefCol.Table.Properties[tbrefCol.CsName]; newinitExpBindings.Add(Expression.Bind(tmpMemberInfo, Expression.MakeMemberAccess(selectExp.Parameters[0], tmpMemberInfo))); } if (subSelect._includeToList.Any()) //如果还有向下 IncludeMany,要把它的主键也查出来 { foreach (var tbrefPkCol in _commonUtils.GetTableByEntity(tbref.RefEntityType).Primarys) { if (newinitExpBindings.Any(a => a.Member.Name == tbrefPkCol.CsName)) continue; var tmpMemberInfo = tbrefPkCol.Table.Properties[tbrefPkCol.CsName]; newinitExpBindings.Add(Expression.Bind(tmpMemberInfo, Expression.MakeMemberAccess(selectExp.Parameters[0], tmpMemberInfo))); } } Expression newinitExp = Expression.MemberInit(tmpinitExp.NewExpression, newinitExpBindings.ToList()); var selectExpParam = subSelect._tables[0].Parameter ?? Expression.Parameter(typeof(TNavigate), subSelectT1Alias); newinitExp = new NewExpressionVisitor(selectExpParam, selectExp.Parameters[0]).Replace(newinitExp); selectExp = Expression.Lambda>(newinitExp, selectExpParam); } switch (tbref.RefType) { case TableRefType.OneToMany: if (true) { var subList = new List(); var tbref2 = _commonUtils.GetTableByEntity(tbref.RefEntityType); Func> getWhereDic = () => { var sbDic = new Dictionary(); for (var y = 0; y < list.Count; y++) { var sbWhereOne = new StringBuilder(); sbWhereOne.Append("("); for (var z = 0; z < tbref.Columns.Count; z++) { if (z > 0) sbWhereOne.Append(" AND "); sbWhereOne.Append(_commonUtils.FormatSql($"{subSelectT1Alias}.{_commonUtils.QuoteSqlName(tbref.RefColumns[z].Attribute.Name)}={{0}}", Utils.GetDataReaderValue(tbref.RefColumns[z].Attribute.MapType, getListValue(list[y], tbref.Columns[z].CsName, z)))); } sbWhereOne.Append(")"); var whereOne = sbWhereOne.ToString(); sbWhereOne.Clear(); if (sbDic.ContainsKey(whereOne) == false) sbDic.Add(whereOne, true); } return sbDic; }; if (takeNumber > 0) { Select0Provider, TNavigate>.GetAllFieldExpressionTreeInfo af = null; ReadAnonymousTypeAfInfo mf = default; if (selectExp == null) af = subSelect.GetAllFieldExpressionTreeLevelAll(); else mf = subSelect.GetExpressionField(selectExp); var sbSql = new StringBuilder(); var sbDic = getWhereDic(); foreach (var sbd in sbDic) { subSelect._where.Clear(); subSelect.Where(sbd.Key).Where(oldWhere).Limit(takeNumber); sbSql.Append("\r\nUNION ALL\r\nselect * from (").Append(subSelect.ToSql(selectExp == null ? af.Field : mf.field)).Append(") ftb"); } sbSql.Remove(0, 13); if (sbDic.Count == 1) sbSql.Remove(0, 15).Remove(sbSql.Length - 5, 5); sbDic.Clear(); if (isAsync) { #if net40 #else if (selectExp == null) subList = await subSelect.ToListAfPrivateAsync(sbSql.ToString(), af, null, cancellationToken); else subList = await subSelect.ToListMrPrivateAsync(sbSql.ToString(), mf, null, cancellationToken); #endif } else { if (selectExp == null) subList = subSelect.ToListAfPrivate(sbSql.ToString(), af, null); else subList = subSelect.ToListMrPrivate(sbSql.ToString(), mf, null); } sbSql.Clear(); } else { subSelect._where.Clear(); if (tbref.Columns.Count == 1) { var arrExp = Expression.NewArrayInit(tbref.RefColumns[0].CsType, list.Select(a => getListValue(a, tbref.Columns[0].CsName, 0)).Distinct() .Select(a => Expression.Constant(Utils.GetDataReaderValue(tbref.RefColumns[0].CsType, a), tbref.RefColumns[0].CsType)).ToArray()); var otmExpParm1 = Expression.Parameter(typeof(TNavigate), "a"); var containsMethod = _dicTypeMethod.GetOrAdd(tbref.RefColumns[0].CsType, et => new ConcurrentDictionary()).GetOrAdd("Contains", mn => typeof(Enumerable).GetMethods().Where(a => a.Name == mn).First()).MakeGenericMethod(tbref.RefColumns[0].CsType); var refCol = Expression.MakeMemberAccess(otmExpParm1, tbref2.Properties[tbref.RefColumns[0].CsName]); //if (refCol.Type.IsNullableType()) refCol = Expression.Property(refCol, CommonExpression._dicNullableValueProperty.GetOrAdd(refCol.Type, ct1 => ct1.GetProperty("Value"))); subSelect.Where(Expression.Lambda>( Expression.Call(null, containsMethod, arrExp, refCol), otmExpParm1)); } else { var sbDic = getWhereDic(); var sbWhere = new StringBuilder(); foreach (var sbd in sbDic) sbWhere.Append(" OR ").Append(sbd.Key); subSelect.Where(sbWhere.Remove(0, 4).ToString()); sbWhere.Clear(); sbDic.Clear(); } subSelect.Where(oldWhere); if (isAsync) { #if net40 #else if (selectExp == null) subList = await subSelect.ToListAsync(true, cancellationToken); else subList = await subSelect.ToListAsync(selectExp, cancellationToken); #endif } else { if (selectExp == null) subList = subSelect.ToList(true); else subList = subSelect.ToList(selectExp); } } fillOneToManyData(subList, tbref2); } break; case TableRefType.ManyToMany: if (true) { List subList = null; List midList = new List(); var tbref2 = _commonUtils.GetTableByEntity(tbref.RefEntityType); var tbrefMid = _commonUtils.GetTableByEntity(tbref.RefMiddleEntityType); var tbrefMidName = _tableRules?.FirstOrDefault()?.Invoke(tbref.RefMiddleEntityType, tbrefMid.DbName) ?? tbrefMid.DbName; var sbJoin = new StringBuilder().Append($"{_commonUtils.QuoteSqlName(tbrefMidName)} midtb ON "); for (var z = 0; z < tbref.RefColumns.Count; z++) { if (z > 0) sbJoin.Append(" AND "); sbJoin.Append($"midtb.{_commonUtils.QuoteSqlName(tbref.MiddleColumns[tbref.Columns.Count + z].Attribute.Name)} = a.{_commonUtils.QuoteSqlName(tbref.RefColumns[z].Attribute.Name)}"); if (_whereGlobalFilter.Any()) { var cascade = _commonExpression.GetWhereCascadeSql(new SelectTableInfo { Alias = "midtb", AliasInit = "midtb", Table = tbrefMid, Type = SelectTableInfoType.InnerJoin }, _whereGlobalFilter, true); if (string.IsNullOrEmpty(cascade) == false) sbJoin.Append(" AND ").Append(cascade); } } subSelect.InnerJoin(sbJoin.ToString()); sbJoin.Clear(); Select0Provider, TNavigate>.GetAllFieldExpressionTreeInfo af = null; ReadAnonymousTypeAfInfo mf = default; if (selectExp == null) af = subSelect.GetAllFieldExpressionTreeLevelAll(); else mf = subSelect.GetExpressionField(selectExp); ReadAnonymousTypeAfInfo otherData = null; var sbSql = new StringBuilder(); if (_selectExpression == null) { var field = new StringBuilder(); var read = new ReadAnonymousTypeInfo(); read.CsType = (tbrefMid.TypeLazy ?? tbrefMid.Type); read.Consturctor = read.CsType.InternalGetTypeConstructor0OrFirst(); read.IsEntity = true; read.Table = tbrefMid; foreach (var col in tbrefMid.Columns.Values) { if (tbref.MiddleColumns.Where(a => a.CsName == col.CsName).Any() == false) continue; var child = new ReadAnonymousTypeInfo { CsName = col.CsName, CsType = col.CsType, DbField = $"midtb.{_commonUtils.QuoteSqlName(col.Attribute.Name)}", MapType = col.Attribute.MapType, Property = tbrefMid.Properties[col.CsName] }; read.Childs.Add(child); field.Append(", ").Append(_commonUtils.RereadColumn(col, child.DbField)); } otherData = new ReadAnonymousTypeAfInfo(read, field.ToString()); } Func> getWhereDic = () => { var sbDic = new Dictionary(); for (var y = 0; y < list.Count; y++) { var sbWhereOne = new StringBuilder(); sbWhereOne.Append("("); for (var z = 0; z < tbref.Columns.Count; z++) { if (z > 0) sbWhereOne.Append(" AND "); sbWhereOne.Append(_commonUtils.FormatSql($" midtb.{_commonUtils.QuoteSqlName(tbref.MiddleColumns[z].Attribute.Name)}={{0}}", Utils.GetDataReaderValue(tbref.MiddleColumns[z].Attribute.MapType, getListValue1(list[y], tbref.Columns[z].CsName)))); } sbWhereOne.Append(")"); var whereOne = sbWhereOne.ToString(); sbWhereOne.Clear(); if (sbDic.ContainsKey(whereOne) == false) sbDic.Add(whereOne, true); } return sbDic; }; if (takeNumber > 0) { var sbDic = getWhereDic(); foreach (var sbd in sbDic) { subSelect._where.Clear(); subSelect.Where(sbd.Key).Where(oldWhere).Limit(takeNumber); sbSql.Append("\r\nUNION ALL\r\nselect * from (").Append(subSelect.ToSql($"{(selectExp == null ? af.Field : mf.field)}{otherData?.field}")).Append(") ftb"); } sbSql.Remove(0, 13); if (sbDic.Count == 1) sbSql.Remove(0, 15).Remove(sbSql.Length - 5, 5); sbDic.Clear(); } else { subSelect._where.Clear(); if (tbref.Columns.Count == 1) { subSelect.Where(_commonUtils.FormatSql($"midtb.{_commonUtils.QuoteSqlName(tbref.MiddleColumns[0].Attribute.Name)} in {{0}}", list.Select(a => Utils.GetDataReaderValue(tbref.MiddleColumns[0].Attribute.MapType, getListValue1(a, tbref.Columns[0].CsName))).Distinct())); } else { var sbDic = getWhereDic(); var sbWhere = new StringBuilder(); foreach (var sbd in sbDic) sbWhere.Append(" OR ").Append(sbd.Key); subSelect.Where(sbWhere.Remove(0, 4).ToString()); sbWhere.Clear(); sbDic.Clear(); } subSelect.Where(oldWhere); sbSql.Append(subSelect.ToSql($"{(selectExp == null ? af.Field : mf.field)}{otherData?.field}")); } if (isAsync) { #if net40 #else if (selectExp == null) subList = await subSelect.ToListAfPrivateAsync(sbSql.ToString(), af, otherData == null ? null : new[] { new ReadAnonymousTypeOtherInfo(otherData.field, otherData.map, midList) }, cancellationToken); else subList = await subSelect.ToListMrPrivateAsync(sbSql.ToString(), mf, otherData == null ? null : new[] { new ReadAnonymousTypeOtherInfo(otherData.field, otherData.map, midList) }, cancellationToken); #endif } else { if (selectExp == null) subList = subSelect.ToListAfPrivate(sbSql.ToString(), af, otherData == null ? null : new[] { new ReadAnonymousTypeOtherInfo(otherData.field, otherData.map, midList) }); else subList = subSelect.ToListMrPrivate(sbSql.ToString(), mf, otherData == null ? null : new[] { new ReadAnonymousTypeOtherInfo(otherData.field, otherData.map, midList) }); } if (subList.Any() == false) { foreach (var item in list) setListValue(item, new List()); return; } Dictionary>>> dicList = new Dictionary>>>(); foreach (var item in list) { if (tbref.Columns.Count == 1) { var dicListKey = getListValue1(item, tbref.Columns[0].CsName)?.ToString(); if (dicListKey == null) continue; var dicListVal = Tuple.Create(item, new List()); if (dicList.TryGetValue(dicListKey, out var items) == false) dicList.Add(dicListKey, items = new List>>()); items.Add(dicListVal); } else { var sb = new StringBuilder(); for (var z = 0; z < tbref.Columns.Count; z++) { if (z > 0) sb.Append("*$*"); sb.Append(getListValue1(item, tbref.Columns[z].CsName)); } var dicListKey = sb.ToString(); var dicListVal = Tuple.Create(item, new List()); if (dicList.TryGetValue(dicListKey, out var items) == false) dicList.Add(dicListKey, items = new List>>()); items.Add(dicListVal); sb.Clear(); } } for (var a = 0; a < subList.Count; a++) { string key = null; if (tbref.Columns.Count == 1) key = _orm.GetEntityValueWithPropertyName(tbref.RefMiddleEntityType, midList[a], tbref.MiddleColumns[0].CsName)?.ToString() ?? ""; else { var sb = new StringBuilder(); for (var z = 0; z < tbref.Columns.Count; z++) { if (z > 0) sb.Append("*$*"); sb.Append(_orm.GetEntityValueWithPropertyName(tbref.RefMiddleEntityType, midList[a], tbref.MiddleColumns[z].CsName)); } key = sb.ToString(); sb.Clear(); } if (dicList.TryGetValue(key, out var t1items) == false) continue; foreach (var t1item in t1items) t1item.Item2.Add(subList[a]); } foreach (var t1items in dicList.Values) foreach (var t1item in t1items) setListValue(t1item.Item1, t1item.Item2); dicList.Clear(); } break; case TableRefType.PgArrayToMany: if (true) { var subList = new List(); var tbref2 = _commonUtils.GetTableByEntity(tbref.RefEntityType); if (tbref.RefColumns[0] == tbref2.Primarys[0]) { var listKeys = list.Select(a => { var arrVal = getListValue(a, tbref.Columns[0].CsName, 0) as Array; if (arrVal == null) return null; var arrObjVal = new object[arrVal.Length]; arrVal.CopyTo(arrObjVal, 0); return arrObjVal; }).ToArray(); var arrExp = Expression.NewArrayInit(tbref.RefColumns[0].CsType, listKeys.Where(a => a != null).SelectMany(a => a).Distinct() .Select(a => Expression.Constant(Utils.GetDataReaderValue(tbref.RefColumns[0].CsType, a), tbref.RefColumns[0].CsType)).ToArray()); var otmExpParm1 = Expression.Parameter(typeof(TNavigate), "a"); var containsMethod = _dicTypeMethod.GetOrAdd(tbref.RefColumns[0].CsType, et => new ConcurrentDictionary()).GetOrAdd("Contains", mn => typeof(Enumerable).GetMethods().Where(a => a.Name == mn).First()).MakeGenericMethod(tbref.RefColumns[0].CsType); var refCol = Expression.MakeMemberAccess(otmExpParm1, tbref2.Properties[tbref.RefColumns[0].CsName]); subSelect.Where(Expression.Lambda>( Expression.Call(null, containsMethod, arrExp, refCol), otmExpParm1)); if (isAsync) { #if net40 #else if (selectExp == null) subList = await subSelect.ToListAsync(true, cancellationToken); else subList = await subSelect.ToListAsync(selectExp, cancellationToken); #endif } else { if (selectExp == null) subList = subSelect.ToList(true); else subList = subSelect.ToList(selectExp); } if (subList.Any() == false) { foreach (var item in list) setListValue(item, new List()); return; } var dicSubList = subList.ToDictionary(a => EntityUtilExtensions.GetEntityValueWithPropertyName(_orm, tbref.RefEntityType, a, tbref.RefColumns[0].CsName)?.ToString(), a => a); var parentNavs = new List(); foreach (var tr2 in tbref2.GetAllTableRef()) { var tr2ref = tr2.Value; if (tr2ref.Exception != null) continue; if (tbref2.ColumnsByCs.ContainsKey(tr2.Key)) continue; if (tbref2.ColumnsByCsIgnore.ContainsKey(tr2.Key)) continue; if (tr2ref.RefType != TableRefType.ManyToOne) continue; if (tr2ref.RefEntityType != tb.Type) continue; if (string.Join(",", tr2ref.Columns.Select(a => a.CsName).OrderBy(a => a)) != string.Join(",", tbref.RefColumns.Select(a => a.CsName).OrderBy(a => a))) continue; //- 修复 IncludeMany 只填充子属性中双向关系的 ManyToOne 对象值;防止把 ManyToOne 多个相同类型的导航属性值都填充了 parentNavs.Add(tr2.Key); } for (var y = 0; y < list.Count; y++) { var item = list[y]; var dicListKeys = listKeys[y]; if (dicListKeys == null) continue; var navs = new List(); foreach (var dlk in dicListKeys) { if (dlk == null) { navs.Add(null); continue; } var dicListKey = dlk.ToString(); dicSubList.TryGetValue(dicListKey, out var nav); navs.Add(nav); } setListValue(item, navs); } dicSubList.Clear(); subList.Clear(); } else if (tbref.Columns[0] == tb.Primarys[0]) { var listKeys = list.Select(a => getListValue(a, tbref.Columns[0].CsName, 0)).Distinct() .Select(a => Utils.GetDataReaderValue(tbref.RefColumns[0].CsType.GetElementType(), a)).ToArray(); var listKeysSql = _commonUtils.GetNoneParamaterSqlValue(subSelect._params, "arrtm", tbref.RefColumns[0], tbref.RefColumns[0].CsType, listKeys); subSelect.Where($"{subSelectT1Alias}.{_commonUtils.QuoteSqlName(tbref.RefColumns[0].Attribute.Name)} && {listKeysSql}"); if (isAsync) { #if net40 #else if (selectExp == null) subList = await subSelect.ToListAsync(true, cancellationToken); else subList = await subSelect.ToListAsync(selectExp, cancellationToken); #endif } else { if (selectExp == null) subList = subSelect.ToList(true); else subList = subSelect.ToList(selectExp); } if (subList.Any() == false) { foreach (var item in list) setListValue(item, new List()); return; } var subListDic = subList.Select(a => { var arrVal = EntityUtilExtensions.GetEntityValueWithPropertyName(_orm, tbref2.Type, a, tbref.RefColumns[0].CsName) as Array; var arrObjVal = new object[arrVal.Length]; arrVal.CopyTo(arrObjVal, 0); return arrObjVal.Select(b => NativeTuple.Create(a, b?.ToString())); }).SelectMany(a => a).GroupBy(a => a.Item2).ToDictionary(a => a.Key, a => a.Select(b => b.Item1).ToList()); foreach (var item in list) { var itemKey = getListValue(item, tbref.Columns[0].CsName, 0)?.ToString(); subListDic.TryGetValue(itemKey, out var navs); setListValue(item, navs); } } } break; } }; #if net40 _includeToList.Add(listObj => includeToListSyncOrAsync(listObj, false)); #else _includeToList.Add(listObj => { var task = includeToListSyncOrAsync(listObj, false, default); if (task.Exception != null) throw task.Exception.InnerException ?? task.Exception; }); _includeToListAsync.Add((listObj, cancellationToken) => includeToListSyncOrAsync(listObj, true, cancellationToken)); #endif var includeValue = new MemberExpression[members.Count + 1]; for (var a = 0; a < members.Count; a++) includeValue[a] = members[a]; includeValue[includeValue.Length - 1] = expBody as MemberExpression; var includeKey = $"{string.Join(".", includeValue.Select(a => a.Member.Name))}"; if (_includeInfo.ContainsKey(includeKey) == false) _includeInfo.Add(includeKey, includeValue); return this; } internal object _includeManySubListOneToManyTempValue1 = null; internal void SetList(IEnumerable list) { foreach (var include in _includeToList) include?.Invoke(list); _trackToList?.Invoke(list); } #if net40 #else async internal Task SetListAsync(IEnumerable list, CancellationToken cancellationToken = default) { foreach (var include in _includeToListAsync) await include?.Invoke(list, cancellationToken); _trackToList?.Invoke(list); } public Task AvgAsync(Expression> column, CancellationToken cancellationToken = default) { if (column == null) return Task.FromResult(default(double)); _tables[0].Parameter = column.Parameters[0]; return this.InternalAvgAsync(column?.Body, cancellationToken); } public Task MaxAsync(Expression> column, CancellationToken cancellationToken = default) { if (column == null) return Task.FromResult(default(TMember)); _tables[0].Parameter = column.Parameters[0]; return this.InternalMaxAsync(column?.Body, cancellationToken); } public Task MinAsync(Expression> column, CancellationToken cancellationToken = default) { if (column == null) return Task.FromResult(default(TMember)); _tables[0].Parameter = column.Parameters[0]; return this.InternalMinAsync(column?.Body, cancellationToken); } public Task SumAsync(Expression> column, CancellationToken cancellationToken = default) { if (column == null) return Task.FromResult(default(decimal)); _tables[0].Parameter = column.Parameters[0]; return this.InternalSumAsync(column?.Body, cancellationToken); } async public Task> ToListAsync(Expression> select, CancellationToken cancellationToken = default) { if (select == null) return await this.InternalToListAsync(select?.Body, cancellationToken); _tables[0].Parameter = select.Parameters[0]; if (_includeToList?.Any() != true) return await this.InternalToListAsync(select.Body, cancellationToken); var findIncludeMany = new List(); //支持指定已经使用 IncudeMany 的导航属性 var map = new ReadAnonymousTypeInfo(); var field = new StringBuilder(); var index = 0; _commonExpression.ReadAnonymousField(_tables, _tableRule, field, map, ref index, select.Body, this, _diymemexpWithTempQuery, _whereGlobalFilter, findIncludeMany, null, true); var af = new ReadAnonymousTypeAfInfo(map, field.Length > 0 ? field.Remove(0, 2).ToString() : null); if (findIncludeMany.Any() == false) return await this.ToListMapReaderPrivateAsync(af, null, cancellationToken); var parmExp = Expression.Parameter(_tables[0].Table.Type, _tables[0].Alias); var incNewInit = new IncludeManyNewInit(_tables[0].Table, parmExp); foreach (var inc in _includeInfo) { var curIncNewInit = incNewInit; Expression curParmExp = parmExp; for (var a = 0; a < inc.Value.Length - 1; a++) { curParmExp = Expression.MakeMemberAccess(parmExp, inc.Value[a].Member); if (curIncNewInit.Childs.ContainsKey(inc.Value[a].Member.Name) == false) curIncNewInit.Childs.Add(inc.Value[a].Member.Name, curIncNewInit = new IncludeManyNewInit(_orm.CodeFirst.GetTableByEntity(inc.Value[a].Type), curParmExp)); else curIncNewInit = curIncNewInit.Childs[inc.Value[a].Member.Name]; } curIncNewInit.IsOutputPrimary = true; } MemberInitExpression GetIncludeManyNewInitExpression(IncludeManyNewInit imni) { var bindings = new List(); if (imni.IsOutputPrimary) bindings.AddRange(imni.Table.Primarys.Select(a => Expression.Bind(imni.Table.Properties[a.CsName], Expression.MakeMemberAccess(imni.CurrentExpression, imni.Table.Properties[a.CsName])))); if (imni.Childs.Any()) bindings.AddRange(imni.Childs.Select(a => Expression.Bind(imni.Table.Properties[a.Key], GetIncludeManyNewInitExpression(a.Value)))); var pgarrayToManys = imni.Table.GetAllTableRef().Select(tr => { if (tr.Value.RefType != TableRefType.PgArrayToMany) return null; var reftb = _orm.CodeFirst.GetTableByEntity(tr.Value.RefEntityType); if (tr.Value.RefColumns[0] == reftb.Primarys[0]) { bindings.Add(Expression.Bind(imni.Table.Properties[tr.Value.Columns[0].CsName], Expression.MakeMemberAccess(imni.CurrentExpression, imni.Table.Properties[tr.Value.Columns[0].CsName]))); return tr.Key; } return null; }).ToList(); return Expression.MemberInit(imni.Table.Type.InternalNewExpression(), bindings); } var otherNewInit = GetIncludeManyNewInitExpression(incNewInit); //获取 IncludeMany 包含的最简化字段 if (otherNewInit.Bindings.Any() == false) return await this.ToListMapReaderPrivateAsync(af, null, cancellationToken); var otherMap = new ReadAnonymousTypeInfo(); field.Clear(); _commonExpression.ReadAnonymousField(_tables, _tableRule, field, otherMap, ref index, otherNewInit, this, _diymemexpWithTempQuery, _whereGlobalFilter, null, null, true); var otherRet = new List(); var otherAf = new ReadAnonymousTypeOtherInfo(field.ToString(), otherMap, otherRet); af.fillIncludeMany = new List>(); var ret = await this.ToListMapReaderPrivateAsync(af, new[] { otherAf }, cancellationToken); await this.SetListAsync(otherRet.Select(a => (T1)a).ToList(), cancellationToken); //级联加载 foreach (var fim in af.fillIncludeMany) { var splitKeys = fim.Item1.Split('.'); var otherRetItem = otherRet[fim.Item3]; var otherRetItemType = _tables[0].Table.Type; foreach (var splitKey in splitKeys) { otherRetItem = _orm.GetEntityValueWithPropertyName(otherRetItemType, otherRetItem, splitKey); otherRetItemType = _orm.CodeFirst.GetTableByEntity(otherRetItemType).Properties[splitKey].PropertyType; } if (otherRetItem == null) continue; var otherList = otherRetItem as IEnumerable; foreach (var otherListItem in otherList) fim.Item2.Add(otherListItem); } return ret; } async public Task> ToListAsync(CancellationToken cancellationToken = default) => typeof(T1) == typeof(TDto) ? await ToListAsync(false, cancellationToken) as List : await ToListAsync(GetToListDtoSelector(), cancellationToken); public Task InsertIntoAsync(string tableName, Expression> select, CancellationToken cancellationToken = default) where TTargetEntity : class => base.InternalInsertIntoAsync(tableName, select, cancellationToken); public Task ToDataTableAsync(Expression> select, CancellationToken cancellationToken = default) { if (select == null) return this.InternalToDataTableAsync(select?.Body, cancellationToken); _tables[0].Parameter = select.Parameters[0]; return this.InternalToDataTableAsync(select?.Body, cancellationToken); } public Task ToAggregateAsync(Expression, TReturn>> select, CancellationToken cancellationToken = default) { if (select == null) return Task.FromResult(default(TReturn)); _tables[0].Parameter = select.Parameters[0]; return this.InternalToAggregateAsync(select?.Body, cancellationToken); } async public Task AnyAsync(Expression> exp, CancellationToken cancellationToken = default) { var oldwhere = _where.ToString(); var ret = await this.Where(exp).AnyAsync(cancellationToken); _where.Clear().Append(oldwhere); return ret; } async public Task ToOneAsync(Expression> select, CancellationToken cancellationToken = default) => (await this.Limit(1).ToListAsync(select, cancellationToken)).FirstOrDefault(); async public Task ToOneAsync(CancellationToken cancellationToken = default) => (await this.Limit(1).ToListAsync(cancellationToken)).FirstOrDefault(); public Task FirstAsync(Expression> select, CancellationToken cancellationToken = default) => this.ToOneAsync(select, cancellationToken); public Task FirstAsync(CancellationToken cancellationToken = default) => this.ToOneAsync(cancellationToken); public override Task> ToListAsync(bool includeNestedMembers, CancellationToken cancellationToken = default) => base.ToListAsync(_isIncluded || includeNestedMembers, cancellationToken); #endif } }