public AssociatedTableContext( [JetBrains.Annotations.NotNull] ExpressionBuilder builder, [JetBrains.Annotations.NotNull] TableContext parent, [JetBrains.Annotations.NotNull] AssociationDescriptor association ) : base(builder, parent.SelectQuery) { if (builder == null) { throw new ArgumentNullException(nameof(builder)); } if (parent == null) { throw new ArgumentNullException(nameof(parent)); } if (association == null) { throw new ArgumentNullException(nameof(association)); } var type = association.MemberInfo.GetMemberType(); var left = association.CanBeNull; if (typeof(IEnumerable).IsSameOrParentOf(type)) { var eTypes = type.GetGenericArguments(typeof(IEnumerable <>)); type = eTypes != null && eTypes.Length > 0 ? eTypes[0] : type.GetListItemType(); IsList = true; } OriginalType = type; ObjectType = GetObjectType(); EntityDescriptor = Builder.MappingSchema.GetEntityDescriptor(ObjectType); InheritanceMapping = EntityDescriptor.InheritanceMapping; SqlTable = new SqlTable(builder.MappingSchema, ObjectType); Association = association; ParentAssociation = parent; SqlJoinedTable join; var queryMethod = Association.GetQueryMethod(parent.ObjectType, ObjectType); if (queryMethod != null) { var selectManyMethod = GetAssociationQueryExpression(Expression.Constant(builder.DataContext), queryMethod.Parameters[0], parent.ObjectType, parent.Expression, queryMethod); var ownerTableSource = SelectQuery.From.Tables[0]; var buildInfo = new BuildInfo(this, selectManyMethod, new SelectQuery()) { IsAssociationBuilt = true }; _innerContext = builder.BuildSequence(buildInfo); if (Association.CanBeNull) { _innerContext = new DefaultIfEmptyBuilder.DefaultIfEmptyContext(buildInfo.Parent, _innerContext, null); } var associationQuery = _innerContext.SelectQuery; if (associationQuery.Select.From.Tables.Count < 1) { throw new LinqToDBException("Invalid association query. It is not possible to inline query."); } var foundIndex = associationQuery.Select.From.Tables.FindIndex(t => t.Source is SqlTable sqlTable && QueryHelper.IsEqualTables(sqlTable, parent.SqlTable)); // try to search table by object type // TODO: review maybe there are another ways to do that if (foundIndex < 0) { foundIndex = associationQuery.Select.From.Tables.FindIndex(t => t.Source is SqlTable sqlTable && sqlTable.ObjectType == parent.SqlTable.ObjectType); } if (foundIndex < 0) { throw new LinqToDBException("Invalid association query. It is not possible to inline query. Can not find owner table."); } var sourceToReplace = associationQuery.Select.From.Tables[foundIndex]; if (left) { foreach (var joinedTable in sourceToReplace.Joins) { if (joinedTable.JoinType == JoinType.Inner) { joinedTable.JoinType = JoinType.Left; } else if (joinedTable.JoinType == JoinType.CrossApply) { joinedTable.JoinType = JoinType.OuterApply; } joinedTable.IsWeak = true; } } ownerTableSource.Joins.AddRange(sourceToReplace.Joins); // prepare fields mapping to replace fields that will be generated by association query _replaceMap = ((SqlTable)sourceToReplace.Source).Fields.Values.ToDictionary(f => (ISqlExpression)f, f => parent.SqlTable.Fields[f.Name]); ownerTableSource.Walk(new WalkOptions(), e => { if (_replaceMap.TryGetValue(e, out var newField)) { return(newField); } return(e); }); ParentAssociationJoin = sourceToReplace.Joins.FirstOrDefault(); join = ParentAssociationJoin; // add rest of tables SelectQuery.From.Tables.AddRange(associationQuery.Select.From.Tables.Where(t => t != sourceToReplace)); //TODO: Change AssociatedTableContext base class //SqlTable = null; } else { var psrc = parent.SelectQuery.From[parent.SqlTable]; join = left ? SqlTable.WeakLeftJoin().JoinedTable : SqlTable.WeakInnerJoin().JoinedTable; ParentAssociationJoin = join; psrc.Joins.Add(join); for (var i = 0; i < association.ThisKey.Length; i++) { if (!parent.SqlTable.Fields.TryGetValue(association.ThisKey[i], out var field1)) { throw new LinqException("Association key '{0}' not found for type '{1}.", association.ThisKey[i], parent.ObjectType); } if (!SqlTable.Fields.TryGetValue(association.OtherKey[i], out var field2)) { throw new LinqException("Association key '{0}' not found for type '{1}.", association.OtherKey[i], ObjectType); } // join.Field(field1).Equal.Field(field2); ISqlPredicate predicate = new SqlPredicate.ExprExpr( field1, SqlPredicate.Operator.Equal, field2); predicate = builder.Convert(parent, predicate); join.Condition.Conditions.Add(new SqlCondition(false, predicate)); } if (ObjectType != OriginalType) { var predicate = Builder.MakeIsPredicate(this, OriginalType); if (predicate.GetType() != typeof(SqlPredicate.Expr)) { join.Condition.Conditions.Add(new SqlCondition(false, predicate)); } } RegularConditionCount = join.Condition.Conditions.Count; ExpressionPredicate = Association.GetPredicate(parent.ObjectType, ObjectType); if (ExpressionPredicate != null) { ExpressionPredicate = (LambdaExpression)Builder.ConvertExpressionTree(ExpressionPredicate); var expr = Builder.ConvertExpression(ExpressionPredicate.Body.Unwrap()); Builder.BuildSearchCondition( new ExpressionContext(parent.Parent, new IBuildContext[] { parent, this }, ExpressionPredicate), expr, join.Condition.Conditions, false); } } if (!association.AliasName.IsNullOrEmpty() && join != null) { join.Table.Alias = association.AliasName; } else { if (!Common.Configuration.Sql.AssociationAlias.IsNullOrEmpty() && join != null) { join.Table.Alias = string.Format(Common.Configuration.Sql.AssociationAlias, association.MemberInfo.Name); } } Init(false); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); var take = 0; if (!buildInfo.IsSubQuery || builder.DataContextInfo.SqlProviderFlags.IsSubQueryTakeSupported) { switch (methodCall.Method.Name) { case "First": case "FirstOrDefault": take = 1; break; case "Single": case "SingleOrDefault": if (!buildInfo.IsSubQuery) { var takeValue = buildInfo.SelectQuery.Select.TakeValue as SqlValue; if (takeValue != null && (int)takeValue.Value >= 2) { take = 2; } } break; } } if (take != 0) { builder.BuildTake(sequence, new SqlValue(take), null); } return(new FirstSingleContext(buildInfo.Parent, sequence, methodCall)); }
protected override bool CanBuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { return (methodCall.IsQueryable(MethodNames) && methodCall.Arguments.Count == 2 || methodCall.IsAsyncExtension(MethodNamesAsync) && methodCall.Arguments.Count == 3); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); var wrapped = false; if (sequence.SelectQuery.Select.HasModifier) { sequence = new SubQueryContext(sequence); wrapped = true; } var isContinuousOrder = !sequence.SelectQuery.OrderBy.IsEmpty && methodCall.Method.Name.StartsWith("Then"); var lambda = (LambdaExpression)methodCall.Arguments[1].Unwrap(); SqlInfo[] sql; while (true) { var sparent = sequence.Parent; var order = new ExpressionContext(buildInfo.Parent, sequence, lambda); var body = lambda.Body.Unwrap(); sql = builder.ConvertExpressions(order, body, ConvertFlags.Key, null); builder.ReplaceParent(order, sparent); // Do not create subquery for ThenByExtensions if (wrapped || isContinuousOrder) { break; } // handle situation when order by uses complex field var isComplex = false; foreach (var sqlInfo in sql) { // immutable expressions will be removed later // var isImmutable = QueryHelper.IsConstant(sqlInfo.Sql); if (isImmutable) { continue; } // possible we have to extend this list // isComplex = null != sqlInfo.Sql.Find(QueryElementType.SqlQuery); if (isComplex) { break; } } if (!isComplex) { break; } sequence = new SubQueryContext(sequence); wrapped = true; } if (!isContinuousOrder && !Configuration.Linq.DoNotClearOrderBys) { sequence.SelectQuery.OrderBy.Items.Clear(); } foreach (var expr in sql) { // we do not need sorting by immutable values, like "Some", Func("Some"), "Some1" + "Some2". It does nothing for ordering // if (QueryHelper.IsConstant(expr.Sql)) { continue; } sequence.SelectQuery.OrderBy.Expr(expr.Sql, methodCall.Method.Name.EndsWith("Descending")); } return(sequence); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0]) { CreateSubQuery = true }); if (sequence.SelectQuery.Select.IsDistinct || sequence.SelectQuery.Select.TakeValue != null || sequence.SelectQuery.Select.SkipValue != null || !sequence.SelectQuery.GroupBy.IsEmpty) { sequence = new SubQueryContext(sequence); } if (sequence.SelectQuery.OrderBy.Items.Count > 0) { if (sequence.SelectQuery.Select.TakeValue == null && sequence.SelectQuery.Select.SkipValue == null) { sequence.SelectQuery.OrderBy.Items.Clear(); } else { sequence = new SubQueryContext(sequence); } } var context = new AggregationContext(buildInfo.Parent, sequence, methodCall); var methodName = methodCall.Method.Name.Replace("Async", ""); var sql = sequence.ConvertToSql(null, 0, ConvertFlags.Field).Select(_ => _.Sql).ToArray(); if (sql.Length == 1 && sql[0] is SelectQuery query) { if (query.Select.Columns.Count == 1) { var join = query.OuterApply(); context.SelectQuery.From.Tables[0].Joins.Add(join.JoinedTable); sql[0] = query.Select.Columns[0]; } } ISqlExpression sqlExpression = new SqlFunction(methodCall.Type, methodName, true, sql); if (sqlExpression == null) { throw new LinqToDBException("Invalid Aggregate function implementation"); } context.Sql = context.SelectQuery; context.FieldIndex = context.SelectQuery.Select.Add(sqlExpression, methodName); return(context); }
public virtual IBuildContext GetContext(Expression expression, int level, BuildInfo buildInfo) { if (expression == null) return this; if (IsScalar) { return ProcessScalar( expression, level, (ctx, ex, l) => ctx.GetContext(ex, l, buildInfo), () => { throw new NotImplementedException(); }); } else { var levelExpression = expression.GetLevelExpression(Builder.MappingSchema, level); switch (levelExpression.NodeType) { case ExpressionType.MemberAccess : { if (levelExpression == expression && Sequence.Length == 1 && !(Sequence[0] is GroupByBuilder.GroupByContext)) { var memberExpression = GetMemberExpression( ((MemberExpression)levelExpression).Member, levelExpression == expression, levelExpression.Type, expression); return GetContext(memberExpression, 0, new BuildInfo(this, memberExpression, buildInfo.SelectQuery)); } var context = ProcessMemberAccess( expression, (MemberExpression)levelExpression, level, (n,ctx,ex,l,_) => n == 0 ? null : ctx.GetContext(ex, l, buildInfo)); if (context == null) throw new NotImplementedException(); return context; } case ExpressionType.Parameter : { var sequence = GetSequence(expression, level); var parameter = Lambda.Parameters[Sequence.Length == 0 ? 0 : Array.IndexOf(Sequence, sequence)]; if (ReferenceEquals(levelExpression, expression)) { if (ReferenceEquals(levelExpression, parameter)) return sequence.GetContext(null, 0, buildInfo); } else if (level == 0) return sequence.GetContext(expression, 1, buildInfo); break; } } if (level == 0) { var sequence = GetSequence(expression, level); return sequence.GetContext(expression, level + 1, buildInfo); } } throw new NotImplementedException(); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var sequence1 = new SubQueryContext(builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0]))); var sequence2 = new SubQueryContext(builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[1], new SelectQuery()))); var union = new SqlUnion(sequence2.SelectQuery, methodCall.Method.Name == "Concat"); sequence1.SelectQuery.Unions.Add(union); return(new UnionContext(sequence1, sequence2, methodCall)); }
public SequenceConvertInfo?Convert(ExpressionBuilder builder, BuildInfo buildInfo, ParameterExpression?param) { return(Convert(builder, (MethodCallExpression)buildInfo.Expression, buildInfo, param)); }
public virtual bool IsSequence(ExpressionBuilder builder, BuildInfo buildInfo) { return(builder.IsSequence(new BuildInfo(buildInfo, ((MethodCallExpression)buildInfo.Expression).Arguments[0]))); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); if (sequence is TableBuilder.TableContext table && table.InheritanceMapping.Count > 0) { var objectType = methodCall.Type.GetGenericArguments()[0]; if (table.ObjectType.IsSameOrParentOf(objectType)) { var predicate = builder.MakeIsPredicate(table, objectType); if (predicate.GetType() != typeof(SqlPredicate.Expr)) { sequence.SelectQuery.Where.SearchCondition.Conditions.Add(new SqlCondition(false, predicate)); } } }
public IBuildContext BuildSequence(ExpressionBuilder builder, BuildInfo buildInfo) { return(BuildMethodCall(builder, (MethodCallExpression)buildInfo.Expression, buildInfo)); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); var collectionSelector = (LambdaExpression)methodCall.Arguments[1].Unwrap(); var resultSelector = (LambdaExpression)methodCall.Arguments[2].Unwrap(); var expr = collectionSelector.Body.Unwrap(); DefaultIfEmptyBuilder.DefaultIfEmptyContext?defaultIfEmpty = null; if (expr is MethodCallExpression mc && AllJoinsBuilder.IsMatchingMethod(mc, true)) { defaultIfEmpty = new DefaultIfEmptyBuilder.DefaultIfEmptyContext(buildInfo.Parent, sequence, null); sequence = new SubQueryContext(defaultIfEmpty); defaultIfEmpty.Disabled = true; }
protected override bool CanBuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { return (methodCall.IsQueryable("SelectMany") && methodCall.Arguments.Count == 3 && ((LambdaExpression)methodCall.Arguments[1].Unwrap()).Parameters.Count == 1); }
public RawSqlContext(ExpressionBuilder builder, BuildInfo buildInfo, Type originalType, string sql, params ISqlExpression[] parameters) : base(builder, buildInfo, new SqlRawSqlTable(builder.MappingSchema, originalType, sql, parameters)) { }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var sequenceExpr = methodCall.Arguments[0]; var sequence = builder.BuildSequence(new BuildInfo(buildInfo, sequenceExpr)); var groupingType = methodCall.Type.GetGenericArgumentsEx()[0]; var keySelector = (LambdaExpression)methodCall.Arguments[1].Unwrap(); var elementSelector = (LambdaExpression)methodCall.Arguments[2].Unwrap(); if (methodCall.Arguments[0].NodeType == ExpressionType.Call) { var call = (MethodCallExpression)methodCall.Arguments[0]; if (call.Method.Name == "Select") { var type = ((LambdaExpression)call.Arguments[1].Unwrap()).Body.Type; if (type.IsGenericTypeEx() && type.GetGenericTypeDefinition() == typeof(ExpressionBuilder.GroupSubQuery <,>)) { sequence = new SubQueryContext(sequence); } } } var key = new KeyContext(buildInfo.Parent, keySelector, sequence); var groupSql = builder.ConvertExpressions(key, keySelector.Body.Unwrap(), ConvertFlags.Key); if (sequence.SelectQuery.Select.IsDistinct || sequence.SelectQuery.GroupBy.Items.Count > 0 || groupSql.Any(_ => !(_.Sql is SqlField || _.Sql is SqlColumn))) { sequence = new SubQueryContext(sequence); key = new KeyContext(buildInfo.Parent, keySelector, sequence); groupSql = builder.ConvertExpressions(key, keySelector.Body.Unwrap(), ConvertFlags.Key); } foreach (var sql in groupSql) { sequence.SelectQuery.GroupBy.Expr(sql.Sql); } // new QueryVisitor().Visit(sequence.SelectQuery.From, e => // { // if (e.ElementType == QueryElementType.JoinedTable) // { // var jt = (SelectQuery.JoinedTable)e; // if (jt.JoinType == SelectQuery.JoinType.Inner) // jt.IsWeak = false; // } // }); var element = new SelectContext(buildInfo.Parent, elementSelector, sequence /*, key*/); var groupBy = new GroupByContext(buildInfo.Parent, sequenceExpr, groupingType, sequence, key, element); Debug.WriteLine("BuildMethodCall GroupBy:\n" + groupBy.SelectQuery); return(groupBy); }
protected abstract bool CanBuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo);
public override IBuildContext GetContext(Expression expression, int level, BuildInfo buildInfo) { if (expression == null && buildInfo != null) { if (buildInfo.Parent is SelectManyBuilder.SelectManyContext) { var sm = (SelectManyBuilder.SelectManyContext)buildInfo.Parent; var ctype = typeof(ContextHelper <>).MakeGenericType(_key.Lambda.Parameters[0].Type); var helper = (IContextHelper)Activator.CreateInstance(ctype); var expr = helper.GetContext( Builder.MappingSchema, Sequence.Expression, _key.Lambda.Parameters[0], Expression.PropertyOrField(sm.Lambda.Parameters[0], "Key"), _key.Lambda.Body); return(Builder.BuildSequence(new BuildInfo(buildInfo, expr))); } //if (buildInfo.Parent == this) { var ctype = typeof(ContextHelper <>).MakeGenericType(_key.Lambda.Parameters[0].Type); var helper = (IContextHelper)Activator.CreateInstance(ctype); var expr = helper.GetContext( Builder.MappingSchema, _sequenceExpr, _key.Lambda.Parameters[0], Expression.PropertyOrField(buildInfo.Expression, "Key"), _key.Lambda.Body); var ctx = Builder.BuildSequence(new BuildInfo(buildInfo, expr)); ctx.SelectQuery.Properties.Add(Tuple.Create("from_group_by", SelectQuery)); return(ctx); } //return this; } if (level != 0) { var levelExpression = expression.GetLevelExpression(Builder.MappingSchema, level); if (levelExpression.NodeType == ExpressionType.MemberAccess) { var ma = (MemberExpression)levelExpression; if (ma.Member.Name == "Key" && ma.Member.DeclaringType == _groupingType) { return(ReferenceEquals(levelExpression, expression) ? _key.GetContext(null, 0, buildInfo) : _key.GetContext(expression, level + 1, buildInfo)); } } } throw new NotImplementedException(); }
protected abstract IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo);
protected override bool CanBuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { return(methodCall.Arguments.Count == 2 && methodCall.IsQueryable("Concat", "Union")); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); var isSubQuery = sequence.SelectQuery.Select.IsDistinct; if (isSubQuery) { sequence = new SubQueryContext(sequence); } if (!(sequence.Statement is SqlInsertStatement insertStatement)) { insertStatement = new SqlInsertStatement(sequence.SelectQuery); sequence.Statement = insertStatement; } var insertType = InsertContext.InsertType.Insert; switch (methodCall.Method.Name) { case nameof(LinqExtensions.Insert): insertType = InsertContext.InsertType.Insert; break; case nameof(LinqExtensions.InsertWithIdentity): insertType = InsertContext.InsertType.InsertWithIdentity; break; case nameof(LinqExtensions.InsertWithOutput): insertType = InsertContext.InsertType.InsertOutput; break; case nameof(LinqExtensions.InsertWithOutputInto): insertType = InsertContext.InsertType.InsertOutputInto; break; }
protected override bool CanBuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { if (!methodCall.IsQueryable(MethodNames)) { return(false); } var body = ((LambdaExpression)methodCall.Arguments[1].Unwrap()).Body.Unwrap(); if (body.NodeType == ExpressionType.MemberInit) { var mi = (MemberInitExpression)body; bool throwExpr; if (mi.NewExpression.Arguments.Count > 0 || mi.Bindings.Count == 0) { throwExpr = true; } else { throwExpr = mi.Bindings.Any(b => b.BindingType != MemberBindingType.Assignment); } if (throwExpr) { throw new NotSupportedException($"Explicit construction of entity type '{body.Type}' in order by is not allowed."); } } return(true); }
protected override bool CanBuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { return(methodCall.IsQueryable("All", "Any")); }
protected override bool CanBuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { if (methodCall.IsQueryable(MethodNames) || methodCall.IsAsyncExtension(MethodNames)) { return(true); } return(false); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0]) { CopyTable = true }); if (methodCall.Arguments.Count == 2) { if (sequence.SelectQuery.Select.TakeValue != null || sequence.SelectQuery.Select.SkipValue != null) { sequence = new SubQueryContext(sequence); } var condition = (LambdaExpression)methodCall.Arguments[1].Unwrap(); if (methodCall.Method.Name == "All") { condition = Expression.Lambda(Expression.Not(condition.Body), condition.Name, condition.Parameters); } sequence = builder.BuildWhere(buildInfo.Parent, sequence, condition, true); sequence.SetAlias(condition.Parameters[0].Name); } return(new AllAnyContext(buildInfo.Parent, methodCall, sequence)); }
protected override SequenceConvertInfo?Convert( ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo, ParameterExpression?param) { return(null); }
public bool CanBuild(ExpressionBuilder builder, BuildInfo buildInfo) { return(Find(builder, buildInfo, (i, t) => i) > 0); }
public override IBuildContext GetContext(Expression expression, int level, BuildInfo buildInfo) { throw new NotImplementedException(); }
public override IBuildContext GetContext(Expression expression, int level, BuildInfo buildInfo) { return(base.GetContext(expression, level, buildInfo)); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); var buildInStatement = false; if (sequence.SelectQuery.Select.TakeValue != null || sequence.SelectQuery.Select.SkipValue != null) { sequence = new SubQueryContext(sequence); buildInStatement = true; } return(new ContainsContext(buildInfo.Parent, methodCall, sequence, buildInStatement)); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); var collectionSelector = (LambdaExpression)methodCall.Arguments[1].Unwrap(); var resultSelector = (LambdaExpression)methodCall.Arguments[2].Unwrap(); if (sequence.SelectQuery.HasUnion || !sequence.SelectQuery.IsSimple) { sequence = new SubQueryContext(sequence); } var context = new SelectManyContext(buildInfo.Parent, collectionSelector, sequence); context.SetAlias(collectionSelector.Parameters[0].Name); var expr = collectionSelector.Body.Unwrap(); var collectionInfo = new BuildInfo(context, expr, new SelectQuery()); var collection = builder.BuildSequence(collectionInfo); if (resultSelector.Parameters.Count > 1) { collection.SetAlias(resultSelector.Parameters[1].Name); } var leftJoin = collection is DefaultIfEmptyBuilder.DefaultIfEmptyContext || collectionInfo.JoinType == JoinType.Left; var sql = collection.SelectQuery; var sequenceTables = new HashSet <ISqlTableSource>(sequence.SelectQuery.From.Tables[0].GetTables()); var newQuery = null != QueryVisitor.Find(sql, e => e == collectionInfo.SelectQuery); var crossApply = null != QueryVisitor.Find(sql, e => e.ElementType == QueryElementType.TableSource && sequenceTables.Contains((ISqlTableSource)e) || e.ElementType == QueryElementType.SqlField && sequenceTables.Contains(((SqlField)e).Table) || e.ElementType == QueryElementType.Column && sequenceTables.Contains(((SqlColumn)e).Parent)); if (collection is JoinBuilder.GroupJoinSubQueryContext queryContext) { var groupJoin = queryContext.GroupJoin; groupJoin.SelectQuery.From.Tables[0].Joins[0].JoinType = JoinType.Inner; groupJoin.SelectQuery.From.Tables[0].Joins[0].IsWeak = false; } if (!newQuery) { if (collection.SelectQuery.Select.HasModifier) { if (crossApply) { var foundJoin = context.SelectQuery.FindJoin(j => j.Table.Source == collection.SelectQuery); if (foundJoin != null) { foundJoin.JoinType = leftJoin ? JoinType.OuterApply : JoinType.CrossApply; collection.SelectQuery.Where.ConcatSearchCondition(foundJoin.Condition); ((ISqlExpressionWalkable)collection.SelectQuery.Where).Walk(false, e => { if (e is SqlColumn column) { if (column.Parent == collection.SelectQuery) { return(column.UnderlyingColumn); } } return(e); }); foundJoin.Condition.Conditions.Clear(); } } } context.Collection = new SubQueryContext(collection, sequence.SelectQuery, false); return(new SelectContext(buildInfo.Parent, resultSelector, sequence, context)); } if (!crossApply) { if (!leftJoin) { context.Collection = new SubQueryContext(collection, sequence.SelectQuery, true); return(new SelectContext(buildInfo.Parent, resultSelector, sequence, context)); } else { var join = sql.OuterApply(); sequence.SelectQuery.From.Tables[0].Joins.Add(join.JoinedTable); context.Collection = new SubQueryContext(collection, sequence.SelectQuery, false); return(new SelectContext(buildInfo.Parent, resultSelector, sequence, context)); } } void MoveSearchConditionsToJoin(SqlFromClause.Join join) { var tableSources = new HashSet <ISqlTableSource>(); ((ISqlExpressionWalkable)sql.Where.SearchCondition).Walk(false, e => { if (e is ISqlTableSource ts && !tableSources.Contains(ts)) { tableSources.Add(ts); } return(e); }); bool ContainsTable(ISqlTableSource tbl, IQueryElement qe) { return(null != QueryVisitor.Find(qe, e => e == tbl || e.ElementType == QueryElementType.SqlField && tbl == ((SqlField)e).Table || e.ElementType == QueryElementType.Column && tbl == ((SqlColumn)e).Parent)); } var conditions = sql.Where.SearchCondition.Conditions; if (conditions.Count > 0) { for (var i = conditions.Count - 1; i >= 0; i--) { var condition = conditions[i]; if (!tableSources.Any(ts => ContainsTable(ts, condition))) { join.JoinedTable.Condition.Conditions.Insert(0, condition); conditions.RemoveAt(i); } } } }