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 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 (methodCall.Arguments[conditionIndex] != null) { var condition = (LambdaExpression)methodCall.Arguments[conditionIndex].Unwrap(); if (sequence.SelectQuery.Select.IsDistinct || sequence.SelectQuery.Select.TakeValue != null || sequence.SelectQuery.Select.SkipValue != null) { sequence = new SubQueryContext(sequence); } 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 condition = (LambdaExpression)methodCall.Arguments[1].Unwrap(); var result = builder.BuildWhere(buildInfo.Parent, sequence, condition, true); result.SetAlias(condition.Parameters[0].Name); return(result); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var deleteType = methodCall.Method.Name switch { nameof(LinqExtensions.DeleteWithOutput) => DeleteContext.DeleteType.DeleteOutput, nameof(LinqExtensions.DeleteWithOutputInto) => DeleteContext.DeleteType.DeleteOutputInto, _ => DeleteContext.DeleteType.Delete, }; var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); if (methodCall.Arguments.Count == 2 && deleteType == DeleteContext.DeleteType.Delete) { sequence = builder.BuildWhere(buildInfo.Parent, sequence, (LambdaExpression)methodCall.Arguments[1].Unwrap(), false); } var deleteStatement = new SqlDeleteStatement(sequence.SelectQuery); sequence.Statement = deleteStatement; // Check association. // if (sequence is SelectContext ctx && ctx.IsScalar) { var res = ctx.IsExpression(null, 0, RequestFor.Association); if (res.Result) { var isTableResult = res.Context !.IsExpression(null, 0, RequestFor.Table); if (!isTableResult.Result) { throw new LinqException("Can not retrieve Table context from association."); } var atc = (TableBuilder.TableContext)isTableResult.Context !; deleteStatement.Table = atc.SqlTable; } else { res = ctx.IsExpression(null, 0, RequestFor.Table); if (res.Result && res.Context is TableBuilder.TableContext context) { var tc = context; if (deleteStatement.SelectQuery.From.Tables.Count == 0 || deleteStatement.SelectQuery.From.Tables[0].Source != tc.SelectQuery) { deleteStatement.Table = tc.SqlTable; } } } }
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; joinType = (SqlJoinType)methodCall.Arguments[1].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[1].EvaluateExpression()!}") }; 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 isHaving = methodCall.Method.Name == "Having"; var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); var condition = (LambdaExpression)methodCall.Arguments[1].Unwrap(); if (sequence.SelectQuery.Select.IsDistinct || sequence.SelectQuery.Select.TakeValue != null || sequence.SelectQuery.Select.SkipValue != null) { sequence = new SubQueryContext(sequence); } var result = builder.BuildWhere(buildInfo.Parent, sequence, condition, !isHaving, isHaving); result.SetAlias(condition.Parameters[0].Name); return(result); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); if (methodCall.Arguments.Count == 2) { sequence = builder.BuildWhere(buildInfo.Parent, sequence, (LambdaExpression)methodCall.Arguments[1].Unwrap(), false); } var deleteStatement = new SqlDeleteStatement(sequence.SelectQuery); sequence.Statement = deleteStatement; // Check association. // if (sequence is SelectContext ctx && ctx.IsScalar) { var res = ctx.IsExpression(null, 0, RequestFor.Association); if (res.Result && res.Context is TableBuilder.AssociatedTableContext) { var atc = (TableBuilder.AssociatedTableContext)res.Context; deleteStatement.Table = atc.SqlTable; } else { res = ctx.IsExpression(null, 0, RequestFor.Table); if (res.Result && res.Context is TableBuilder.TableContext) { var tc = (TableBuilder.TableContext)res.Context; if (deleteStatement.SelectQuery.From.Tables.Count == 0 || deleteStatement.SelectQuery.From.Tables[0].Source != tc.SelectQuery) { deleteStatement.Table = tc.SqlTable; } } } } return(new DeleteContext(buildInfo.Parent, sequence)); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); var updateStatement = sequence.Statement as SqlUpdateStatement ?? new SqlUpdateStatement(sequence.SelectQuery); sequence.Statement = updateStatement; switch (methodCall.Arguments.Count) { case 1: // int Update<T>(this IUpdateable<T> source) { CheckAssociation(sequence); break; } case 2: // int Update<T>(this IQueryable<T> source, Expression<Func<T,T>> setter) { CheckAssociation(sequence); if (sequence.SelectQuery.Select.SkipValue != null || !sequence.SelectQuery.Select.OrderBy.IsEmpty) { sequence = new SubQueryContext(sequence); updateStatement.SelectQuery = sequence.SelectQuery; sequence.Statement = updateStatement; } BuildSetter( builder, buildInfo, (LambdaExpression)methodCall.Arguments[1].Unwrap(), sequence, updateStatement.Update.Items, sequence); break; } case 3: { var expr = methodCall.Arguments[1].Unwrap(); if (expr is LambdaExpression lex && lex.ReturnType == typeof(bool)) { CheckAssociation(sequence); // int Update<T>(this IQueryable<T> source, Expression<Func<T,bool>> predicate, Expression<Func<T,T>> setter) // sequence = builder.BuildWhere(buildInfo.Parent, sequence, (LambdaExpression)methodCall.Arguments[1].Unwrap(), false); if (sequence.SelectQuery.Select.SkipValue != null || !sequence.SelectQuery.Select.OrderBy.IsEmpty) { sequence = new SubQueryContext(sequence); } updateStatement.SelectQuery = sequence.SelectQuery; sequence.Statement = updateStatement; BuildSetter( builder, buildInfo, (LambdaExpression)methodCall.Arguments[2].Unwrap(), sequence, updateStatement.Update.Items, sequence); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var deleteType = methodCall.Method.Name switch { nameof(LinqExtensions.DeleteWithOutput) => DeleteContext.DeleteType.DeleteOutput, nameof(LinqExtensions.DeleteWithOutputInto) => DeleteContext.DeleteType.DeleteOutputInto, _ => DeleteContext.DeleteType.Delete, }; var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); if (methodCall.Arguments.Count == 2 && deleteType == DeleteContext.DeleteType.Delete) { sequence = builder.BuildWhere(buildInfo.Parent, sequence, (LambdaExpression)methodCall.Arguments[1].Unwrap(), false); } var deleteStatement = new SqlDeleteStatement(sequence.SelectQuery); sequence.Statement = deleteStatement; // Check association. // if (sequence is SelectContext ctx && ctx.IsScalar) { var res = ctx.IsExpression(null, 0, RequestFor.Association); if (res.Result) { var isTableResult = res.Context !.IsExpression(null, 0, RequestFor.Table); if (!isTableResult.Result) { throw new LinqException("Can not retrieve Table context from association."); } var atc = (TableBuilder.TableContext)isTableResult.Context !; deleteStatement.Table = atc.SqlTable; } else { res = ctx.IsExpression(null, 0, RequestFor.Table); if (res.Result && res.Context is TableBuilder.TableContext context) { var tc = context; if (deleteStatement.SelectQuery.From.Tables.Count == 0 || deleteStatement.SelectQuery.From.Tables[0].Source != tc.SelectQuery) { deleteStatement.Table = tc.SqlTable; } } } } var indexedParameters = methodCall.Method.GetParameters().Select((p, i) => Tuple.Create(p, i)).ToDictionary(t => t.Item1.Name, t => t.Item2); Expression GetArgumentByName(string name) { return(methodCall.Arguments[indexedParameters[name]]); } LambdaExpression GetOutputExpression(Type outputType) { if (!indexedParameters.TryGetValue("outputExpression", out var index)) { var param = Expression.Parameter(outputType); return(Expression.Lambda(param, param)); } return((LambdaExpression)methodCall.Arguments[index].Unwrap()); } IBuildContext? outputContext = null; LambdaExpression?outputExpression = null; if (deleteType != DeleteContext.DeleteType.Delete) { outputExpression = GetOutputExpression(methodCall.Method.GetGenericArguments().Last()); deleteStatement.Output = new SqlOutputClause(); var deletedTable = SqlTable.Deleted(methodCall.Method.GetGenericArguments()[0]); outputContext = new TableBuilder.TableContext(builder, new SelectQuery(), deletedTable); deleteStatement.Output.DeletedTable = deletedTable; if (deleteType == DeleteContext.DeleteType.DeleteOutputInto) { var outputTable = GetArgumentByName("outputTable"); var destination = builder.BuildSequence(new BuildInfo(buildInfo, outputTable, new SelectQuery())); UpdateBuilder.BuildSetter( builder, buildInfo, outputExpression, destination, deleteStatement.Output.OutputItems, outputContext); deleteStatement.Output.OutputTable = ((TableBuilder.TableContext)destination).SqlTable; } } if (deleteType == DeleteContext.DeleteType.DeleteOutput) { return(new DeleteWithOutputContext(buildInfo.Parent, sequence, outputContext !, outputExpression !)); } return(new DeleteContext(buildInfo.Parent, sequence)); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); switch (methodCall.Arguments.Count) { case 1: // int Update<T>(this IUpdateable<T> source) CheckAssociation(sequence); break; case 2: // int Update<T>(this IQueryable<T> source, Expression<Func<T,T>> setter) { CheckAssociation(sequence); if (sequence.SelectQuery.Select.SkipValue != null || !sequence.SelectQuery.Select.OrderBy.IsEmpty) { sequence = new SubQueryContext(sequence); } BuildSetter( builder, buildInfo, (LambdaExpression)methodCall.Arguments[1].Unwrap(), sequence, sequence.SelectQuery.Update.Items, sequence); break; } case 3: { var expr = methodCall.Arguments[1].Unwrap(); if (expr is LambdaExpression && ((LambdaExpression)expr).ReturnType == typeof(bool)) { CheckAssociation(sequence); // int Update<T>(this IQueryable<T> source, Expression<Func<T,bool>> predicate, Expression<Func<T,T>> setter) // sequence = builder.BuildWhere(buildInfo.Parent, sequence, (LambdaExpression)methodCall.Arguments[1].Unwrap(), false); if (sequence.SelectQuery.Select.SkipValue != null || !sequence.SelectQuery.Select.OrderBy.IsEmpty) { sequence = new SubQueryContext(sequence); } BuildSetter( builder, buildInfo, (LambdaExpression)methodCall.Arguments[2].Unwrap(), sequence, sequence.SelectQuery.Update.Items, sequence); } else { IBuildContext into; if (expr is LambdaExpression) { // static int Update<TSource,TTarget>(this IQueryable<TSource> source, Expression<Func<TSource,TTarget>> target, Expression<Func<TSource,TTarget>> setter) // var body = ((LambdaExpression)expr).Body; int level = body.GetLevel(); var tableInfo = sequence.IsExpression(body, level, RequestFor.Table); if (tableInfo.Result == false) { throw new LinqException("Expression '{0}' must be a table."); } into = tableInfo.Context; } else { // static int Update<TSource,TTarget>(this IQueryable<TSource> source, Table<TTarget> target, Expression<Func<TSource,TTarget>> setter) // into = builder.BuildSequence(new BuildInfo(buildInfo, expr, new SelectQuery())); } sequence.ConvertToIndex(null, 0, ConvertFlags.All); new SelectQueryOptimizer(builder.DataContext.SqlProviderFlags, sequence.SelectQuery) .ResolveWeakJoins(new List <ISqlTableSource>()); sequence.SelectQuery.Select.Columns.Clear(); BuildSetter( builder, buildInfo, (LambdaExpression)methodCall.Arguments[2].Unwrap(), into, sequence.SelectQuery.Update.Items, sequence); var sql = sequence.SelectQuery; sql.Select.Columns.Clear(); foreach (var item in sql.Update.Items) { sql.Select.Columns.Add(new SelectQuery.Column(sql, item.Expression)); } sql.Update.Table = ((TableBuilder.TableContext)into).SqlTable; } break; } } sequence.SelectQuery.ChangeQueryType(QueryType.Update); return(new UpdateContext(buildInfo.Parent, sequence)); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); switch (methodCall.Arguments.Count) { case 1: // int Update<T>(this IUpdateable<T> source) CheckAssociation(sequence); break; case 2: // int Update<T>(this IQueryable<T> source, Expression<Func<T,T>> setter) { CheckAssociation(sequence); BuildSetter( builder, buildInfo, (LambdaExpression)methodCall.Arguments[1].Unwrap(), sequence, sequence.SelectQuery.Update.Items, sequence); break; } case 3: { var expr = methodCall.Arguments[1].Unwrap(); if (expr is LambdaExpression) { CheckAssociation(sequence); // int Update<T>(this IQueryable<T> source, Expression<Func<T,bool>> predicate, Expression<Func<T,T>> setter) // sequence = builder.BuildWhere(buildInfo.Parent, sequence, (LambdaExpression)methodCall.Arguments[1].Unwrap(), false); BuildSetter( builder, buildInfo, (LambdaExpression)methodCall.Arguments[2].Unwrap(), sequence, sequence.SelectQuery.Update.Items, sequence); } else { // static int Update<TSource,TTarget>(this IQueryable<TSource> source, Table<TTarget> target, Expression<Func<TSource,TTarget>> setter) // var into = builder.BuildSequence(new BuildInfo(buildInfo, expr, new SelectQuery())); sequence.ConvertToIndex(null, 0, ConvertFlags.All); new SelectQueryOptimizer(builder.DataContextInfo.SqlProviderFlags, sequence.SelectQuery) .ResolveWeakJoins(new List <ISqlTableSource>()); sequence.SelectQuery.Select.Columns.Clear(); BuildSetter( builder, buildInfo, (LambdaExpression)methodCall.Arguments[2].Unwrap(), into, sequence.SelectQuery.Update.Items, sequence); var sql = sequence.SelectQuery; sql.Select.Columns.Clear(); foreach (var item in sql.Update.Items) { sql.Select.Columns.Add(new SelectQuery.Column(sql, item.Expression)); } sql.Update.Table = ((TableBuilder.TableContext)into).SqlTable; } break; } } sequence.SelectQuery.QueryType = QueryType.Update; return(new UpdateContext(buildInfo.Parent, sequence)); }