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);
            }
Example #3
0
        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));
        }
Example #4
0
        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));
        }
Example #5
0
        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));
        }
Example #6
0
        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));
        }