protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); JoinType joinType; var conditionIndex = 1; switch (methodCall.Method.Name) { case "InnerJoin": joinType = JoinType.Inner; break; case "LeftJoin": joinType = JoinType.Left; break; case "RightJoin": joinType = JoinType.Right; break; case "FullJoin": joinType = JoinType.Full; break; default: conditionIndex = 2; var joinValue = (SqlJoinType)methodCall.Arguments[1].EvaluateExpression() !; switch (joinValue) { case SqlJoinType.Inner: joinType = JoinType.Inner; break; case SqlJoinType.Left: joinType = JoinType.Left; break; case SqlJoinType.Right: joinType = JoinType.Right; break; case SqlJoinType.Full: joinType = JoinType.Full; break; default: throw new ArgumentOutOfRangeException(); } break; } buildInfo.JoinType = joinType; if (joinType == JoinType.Left || joinType == JoinType.Full) { sequence = new DefaultIfEmptyBuilder.DefaultIfEmptyContext(buildInfo.Parent, sequence, null); } sequence = new SubQueryContext(sequence); if (methodCall.Arguments[conditionIndex] != null) { var condition = (LambdaExpression)methodCall.Arguments[conditionIndex].Unwrap(); var result = builder.BuildWhere(buildInfo.Parent, sequence, condition, false, false); result.SetAlias(condition.Parameters[0].Name); return(result); } return(sequence); }
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 IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var outerContext = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); var innerContext = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[1], new SelectQuery())); JoinType joinType; var conditionIndex = 2; switch (methodCall.Method.Name) { case "InnerJoin": joinType = JoinType.Inner; break; case "CrossJoin": joinType = JoinType.Inner; conditionIndex = -1; break; case "LeftJoin": joinType = JoinType.Left; break; case "RightJoin": joinType = JoinType.Right; break; case "FullJoin": joinType = JoinType.Full; break; default: conditionIndex = 3; var joinValue = (SqlJoinType)methodCall.Arguments[2].EvaluateExpression(); switch (joinValue) { case SqlJoinType.Inner: joinType = JoinType.Inner; break; case SqlJoinType.Left: joinType = JoinType.Left; break; case SqlJoinType.Right: joinType = JoinType.Right; break; case SqlJoinType.Full: joinType = JoinType.Full; break; default: throw new ArgumentOutOfRangeException(); } break; } if (joinType == JoinType.Right || joinType == JoinType.Full) { outerContext = new DefaultIfEmptyBuilder.DefaultIfEmptyContext(buildInfo.Parent, outerContext, null); } outerContext = new SubQueryContext(outerContext); if (joinType == JoinType.Left || joinType == JoinType.Full) { innerContext = new DefaultIfEmptyBuilder.DefaultIfEmptyContext(buildInfo.Parent, innerContext, null); } innerContext = new SubQueryContext(innerContext); var selector = (LambdaExpression)methodCall.Arguments[methodCall.Arguments.Count - 1].Unwrap(); outerContext.SetAlias(selector.Parameters[0].Name); innerContext.SetAlias(selector.Parameters[1].Name); var joinContext = new JoinContext(buildInfo.Parent, selector, outerContext, innerContext) #if DEBUG { MethodCall = methodCall } #endif ; if (conditionIndex != -1) { var condition = (LambdaExpression)methodCall.Arguments[conditionIndex].Unwrap(); var conditionExpr = condition.GetBody(selector.Parameters[0], selector.Parameters[1]); conditionExpr = builder.ConvertExpression(conditionExpr); var join = new SqlFromClause.Join(joinType, innerContext.SelectQuery, null, false, Array <SqlFromClause.Join> .Empty); outerContext.SelectQuery.From.Tables[0].Joins.Add(join.JoinedTable); builder.BuildSearchCondition( joinContext, conditionExpr, join.JoinedTable.Condition.Conditions, false); } else { outerContext.SelectQuery.From.Table(innerContext.SelectQuery); } return(joinContext); }
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 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); defaultIfEmpty.Disabled = true; sequence = new SubQueryContext(defaultIfEmpty); } var context = new SelectManyContext(buildInfo.Parent, collectionSelector, sequence); context.SetAlias(collectionSelector.Parameters[0].Name); var collectionInfo = new BuildInfo(context, expr, new SelectQuery()); var collection = builder.BuildSequence(collectionInfo); if (resultSelector.Parameters.Count > 1) { collection.SetAlias(resultSelector.Parameters[1].Name); } if (defaultIfEmpty != null && (collectionInfo.JoinType == JoinType.Right || collectionInfo.JoinType == JoinType.Full)) { defaultIfEmpty.Disabled = false; } 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(new WalkOptions(), 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(new WalkOptions(), 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); } } } }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var outerContext = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); var innerContext = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[1], new SelectQuery())); List <SqlQueryExtension>?extensions = null; if (innerContext is QueryExtensionBuilder.JoinHintContext jhc) { innerContext = jhc.Context; extensions = jhc.Extensions; } JoinType joinType; var conditionIndex = 2; switch (methodCall.Method.Name) { case "InnerJoin": joinType = JoinType.Inner; break; case "CrossJoin": joinType = JoinType.Inner; conditionIndex = -1; break; case "LeftJoin": joinType = JoinType.Left; break; case "RightJoin": joinType = JoinType.Right; break; case "FullJoin": joinType = JoinType.Full; break; default: conditionIndex = 3; joinType = (SqlJoinType)methodCall.Arguments[2].EvaluateExpression() !switch { SqlJoinType.Inner => JoinType.Inner, SqlJoinType.Left => JoinType.Left, SqlJoinType.Right => JoinType.Right, SqlJoinType.Full => JoinType.Full, _ => throw new InvalidOperationException($"Unexpected join type: {(SqlJoinType)methodCall.Arguments[2].EvaluateExpression()!}") }; break; } if (joinType == JoinType.Right || joinType == JoinType.Full) { outerContext = new DefaultIfEmptyBuilder.DefaultIfEmptyContext(buildInfo.Parent, outerContext, null); } outerContext = new SubQueryContext(outerContext); if (joinType == JoinType.Left || joinType == JoinType.Full) { innerContext = new DefaultIfEmptyBuilder.DefaultIfEmptyContext(buildInfo.Parent, innerContext, null); } innerContext = new SubQueryContext(innerContext); var selector = (LambdaExpression)methodCall.Arguments[methodCall.Arguments.Count - 1].Unwrap(); outerContext.SetAlias(selector.Parameters[0].Name); innerContext.SetAlias(selector.Parameters[1].Name); var joinContext = new JoinContext(buildInfo.Parent, selector, outerContext, innerContext) #if DEBUG { Debug_MethodCall = methodCall } #endif ; if (conditionIndex != -1) { var condition = (LambdaExpression)methodCall.Arguments[conditionIndex].Unwrap(); var conditionExpr = condition.GetBody(selector.Parameters[0], selector.Parameters[1]); conditionExpr = builder.ConvertExpression(conditionExpr); var join = new SqlFromClause.Join(joinType, innerContext.SelectQuery, null, false, Array <SqlFromClause.Join> .Empty); outerContext.SelectQuery.From.Tables[0].Joins.Add(join.JoinedTable); if (extensions != null) { join.JoinedTable.SqlQueryExtensions = extensions; } builder.BuildSearchCondition( joinContext, conditionExpr, @join.JoinedTable.Condition.Conditions); } else { outerContext.SelectQuery.From.Table(innerContext.SelectQuery); } return(joinContext); }