protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { // UpdateWhenNotMatchedBySourceAnd(merge, searchCondition, setter) var mergeContext = (MergeContext)builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); var statement = mergeContext.Merge; var operation = new SqlMergeOperationClause(MergeOperationType.UpdateBySource); statement.Operations.Add(operation); Expression predicate = methodCall.Arguments[1]; Expression setter = methodCall.Arguments[2]; UpdateBuilder.BuildSetter( builder, buildInfo, (LambdaExpression)setter.Unwrap(), mergeContext, operation.Items, mergeContext); if (!predicate.IsNullValue()) { var condition = (LambdaExpression)predicate.Unwrap(); operation.Where = BuildSearchCondition(builder, statement, mergeContext.TargetContext, null, condition); } return(mergeContext); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { // UpdateWhenNotMatchedBySourceAnd(merge, searchCondition, setter) var mergeContext = (MergeContext)builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); var statement = mergeContext.Merge; var operation = new SqlMergeOperationClause(MergeOperationType.UpdateBySource); statement.Operations.Add(operation); Expression predicate = methodCall.Arguments[1]; Expression setter = methodCall.Arguments[2]; UpdateBuilder.BuildSetter( builder, buildInfo, (LambdaExpression)setter.Unwrap(), mergeContext, operation.Items, mergeContext); if (!(predicate is ConstantExpression constPredicate) || constPredicate.Value != null) { var condition = (LambdaExpression)predicate.Unwrap(); var conditionExpr = builder.ConvertExpression(condition.Body.Unwrap()); operation.Where = new SqlSearchCondition(); builder.BuildSearchCondition( new ExpressionContext(null, new[] { mergeContext.TargetContext }, condition), conditionExpr, operation.Where.Conditions, false); } return(mergeContext); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); var insertOrUpdateStatement = new SqlInsertOrUpdateStatement(sequence.SelectQuery); sequence.Statement = insertOrUpdateStatement; UpdateBuilder.BuildSetter( builder, buildInfo, (LambdaExpression)methodCall.Arguments[1].Unwrap(), sequence, insertOrUpdateStatement.Insert.Items, sequence); UpdateBuilder.BuildSetter( builder, buildInfo, (LambdaExpression)methodCall.Arguments[2].Unwrap(), sequence, insertOrUpdateStatement.Update.Items, sequence); insertOrUpdateStatement.Insert.Into = ((TableBuilder.TableContext)sequence).SqlTable; insertOrUpdateStatement.Update.Table = ((TableBuilder.TableContext)sequence).SqlTable; insertOrUpdateStatement.SelectQuery.From.Tables.Clear(); insertOrUpdateStatement.SelectQuery.From.Table(insertOrUpdateStatement.Update.Table); if (methodCall.Arguments.Count == 3) { var table = insertOrUpdateStatement.Insert.Into; var keys = table.GetKeys(false); if (keys.Count == 0) { throw new LinqException("InsertOrUpdate method requires the '{0}' table to have a primary key.", table.Name); } var q = ( from k in keys join i in insertOrUpdateStatement.Insert.Items on k equals i.Column select new { k, i } ).ToList(); var missedKey = keys.Except(q.Select(i => i.k)).FirstOrDefault(); if (missedKey != null) { throw new LinqException("InsertOrUpdate method requires the '{0}.{1}' field to be included in the insert setter.", table.Name, ((SqlField)missedKey).Name); } insertOrUpdateStatement.Update.Keys.AddRange(q.Select(i => i.i)); } else { UpdateBuilder.BuildSetter( builder, buildInfo, (LambdaExpression)methodCall.Arguments[3].Unwrap(), sequence, insertOrUpdateStatement.Update.Keys, sequence); } return(new InsertOrUpdateContext(buildInfo.Parent, sequence)); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); var isSubQuery = sequence.SqlQuery.Select.IsDistinct; if (isSubQuery) { sequence = new SubQueryContext(sequence); } switch (methodCall.Arguments.Count) { case 1: // static int Insert<T> (this IValueInsertable<T> source) // static int Insert<TSource,TTarget>(this ISelectInsertable<TSource,TTarget> source) { foreach (var item in sequence.SqlQuery.Insert.Items) { sequence.SqlQuery.Select.Expr(item.Expression); } break; } case 2: // static int Insert<T>(this Table<T> target, Expression<Func<T>> setter) { UpdateBuilder.BuildSetter( builder, buildInfo, (LambdaExpression)methodCall.Arguments[1].Unwrap(), sequence, sequence.SqlQuery.Insert.Items, sequence); sequence.SqlQuery.Insert.Into = ((TableBuilder.TableContext)sequence).SqlTable; sequence.SqlQuery.From.Tables.Clear(); break; } case 3: // static int Insert<TSource,TTarget>(this IQueryable<TSource> source, Table<TTarget> target, Expression<Func<TSource,TTarget>> setter) { var into = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[1], new SqlQuery())); UpdateBuilder.BuildSetter( builder, buildInfo, (LambdaExpression)methodCall.Arguments[2].Unwrap(), into, sequence.SqlQuery.Insert.Items, sequence); sequence.SqlQuery.Select.Columns.Clear(); foreach (var item in sequence.SqlQuery.Insert.Items) { sequence.SqlQuery.Select.Columns.Add(new SqlQuery.Column(sequence.SqlQuery, item.Expression)); } sequence.SqlQuery.Insert.Into = ((TableBuilder.TableContext)into).SqlTable; break; } } var insert = sequence.SqlQuery.Insert; var q = insert.Into.Fields.Values.Except(insert.Items.Select(e => e.Column)) .OfType <SqlField>() .Where(f => f.IsIdentity); foreach (var field in q) { var expr = builder.SqlProvider.GetIdentityExpression(insert.Into, field, false); if (expr != null) { insert.Items.Insert(0, new SqlQuery.SetExpression(field, expr)); if (methodCall.Arguments.Count == 3) { sequence.SqlQuery.Select.Columns.Insert(0, new SqlQuery.Column(sequence.SqlQuery, insert.Items[0].Expression)); } } } sequence.SqlQuery.QueryType = QueryType.Insert; sequence.SqlQuery.Insert.WithIdentity = methodCall.Method.Name == "InsertWithIdentity"; return(new InsertContext(buildInfo.Parent, sequence, sequence.SqlQuery.Insert.WithIdentity)); }
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 "Insert": insertType = InsertContext.InsertType.Insert; break; case "InsertWithIdentity": insertType = InsertContext.InsertType.InsertWithIdentity; break; case "InsertWithOutput": insertType = InsertContext.InsertType.InsertOutput; break; case "InsertWithOutputInto": insertType = InsertContext.InsertType.InsertOutputInto; break; } 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 (methodCall.Arguments.Count > 0) { var argument = methodCall.Arguments[0]; if (typeof(IValueInsertable <>).IsSameOrParentOf(argument.Type) || typeof(ISelectInsertable <,>).IsSameOrParentOf(argument.Type)) { // static int Insert<T> (this IValueInsertable<T> source) // static int Insert<TSource,TTarget>(this ISelectInsertable<TSource,TTarget> source) sequence.SelectQuery.Select.Columns.Clear(); foreach (var item in insertStatement.Insert.Items) { sequence.SelectQuery.Select.ExprNew(item.Expression !); } } else if (methodCall.Arguments.Count > 1 && typeof(IQueryable <>).IsSameOrParentOf(argument.Type) && typeof(ITable <>).IsSameOrParentOf(methodCall.Arguments[1].Type)) { // static int Insert<TSource,TTarget>(this IQueryable<TSource> source, Table<TTarget> target, Expression<Func<TSource,TTarget>> setter) var into = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[1], new SelectQuery())); var setter = (LambdaExpression)GetArgumentByName("setter").Unwrap(); UpdateBuilder.BuildSetter( builder, buildInfo, setter, into, insertStatement.Insert.Items, sequence); sequence.SelectQuery.Select.Columns.Clear(); foreach (var item in insertStatement.Insert.Items) { sequence.SelectQuery.Select.Columns.Add(new SqlColumn(sequence.SelectQuery, item.Expression !)); } insertStatement.Insert.Into = ((TableBuilder.TableContext)into).SqlTable; } else if (typeof(ITable <>).IsSameOrParentOf(argument.Type)) { // static int Insert<T>(this Table<T> target, Expression<Func<T>> setter) // static TTarget InsertWithOutput<TTarget>(this ITable<TTarget> target, Expression<Func<TTarget>> setter) // static TTarget InsertWithOutput<TTarget>(this ITable<TTarget> target, Expression<Func<TTarget>> setter, Expression<Func<TTarget,TOutput>> outputExpression) var argIndex = 1; var arg = methodCall.Arguments[argIndex].Unwrap(); LambdaExpression?setter = null; switch (arg) { case LambdaExpression lambda: { setter = lambda; UpdateBuilder.BuildSetter( builder, buildInfo, setter, sequence, insertStatement.Insert.Items, sequence); break; } default: { var objType = arg.Type; var ed = builder.MappingSchema.GetEntityDescriptor(objType); var into = sequence; var ctx = new TableBuilder.TableContext(builder, buildInfo, objType); var table = new SqlTable(objType); foreach (ColumnDescriptor c in ed.Columns.Where(c => !c.SkipOnInsert)) { if (!table.Fields.TryGetValue(c.ColumnName, out var field)) { continue; } var pe = Expression.MakeMemberAccess(arg, c.MemberInfo); var column = into.ConvertToSql(pe, 1, ConvertFlags.Field); var parameter = builder.BuildParameterFromArgumentProperty(methodCall, argIndex, field.ColumnDescriptor); insertStatement.Insert.Items.Add(new SqlSetExpression(column[0].Sql, parameter.SqlParameter)); } var insertedTable = SqlTable.Inserted(methodCall.Method.GetGenericArguments()[0]); break; } } insertStatement.Insert.Into = ((TableBuilder.TableContext)sequence).SqlTable; sequence.SelectQuery.From.Tables.Clear(); } if (insertType == InsertContext.InsertType.InsertOutput || insertType == InsertContext.InsertType.InsertOutputInto) { outputExpression = GetOutputExpression(methodCall.Method.GetGenericArguments().Last()); insertStatement.Output = new SqlOutputClause(); var insertedTable = SqlTable.Inserted(outputExpression.Parameters[0].Type); outputContext = new TableBuilder.TableContext(builder, new SelectQuery(), insertedTable); insertStatement.Output.InsertedTable = insertedTable; if (insertType == InsertContext.InsertType.InsertOutputInto) { var outputTable = GetArgumentByName("outputTable"); var destination = builder.BuildSequence(new BuildInfo(buildInfo, outputTable, new SelectQuery())); UpdateBuilder.BuildSetter( builder, buildInfo, outputExpression, destination, insertStatement.Output.OutputItems, outputContext); insertStatement.Output.OutputTable = ((TableBuilder.TableContext)destination).SqlTable; } } } var insert = insertStatement.Insert; var q = insert.Into !.Fields.Values .Except(insert.Items.Select(e => e.Column)) .OfType <SqlField>() .Where(f => f.IsIdentity); foreach (var field in q) { var expr = builder.DataContext.CreateSqlProvider().GetIdentityExpression(insert.Into); if (expr != null) { insert.Items.Insert(0, new SqlSetExpression(field, expr)); if (methodCall.Arguments.Count == 3) { sequence.SelectQuery.Select.Columns.Insert(0, new SqlColumn(sequence.SelectQuery, insert.Items[0].Expression !)); } } } insertStatement.Insert.WithIdentity = insertType == InsertContext.InsertType.InsertWithIdentity; sequence.Statement = insertStatement; if (insertType == InsertContext.InsertType.InsertOutput) { return(new InsertWithOutputContext(buildInfo.Parent, sequence, outputContext !, outputExpression !)); } return(new InsertContext(buildInfo.Parent, sequence, insertType, outputExpression)); }
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)); }