コード例 #1
0
        IBuildContext ApplyQueryFilters(ExpressionBuilder builder, BuildInfo buildInfo, MemberInfo?memberInfo, TableContext tableContext)
        {
            var entityType = tableContext.ObjectType;

            if (builder.IsFilterDisabled(entityType))
            {
                return(tableContext);
            }

            var ed         = builder.MappingSchema.GetEntityDescriptor(entityType);
            var filterFunc = ed.QueryFilterFunc;

            if (filterFunc == null)
            {
                return(tableContext);
            }

            if (memberInfo == null)
            {
                memberInfo = Methods.LinqToDB.GetTable.MakeGenericMethod(entityType);
            }

            var fakeQuery = ExpressionQueryImpl.CreateQuery(entityType, builder.DataContext, null);

            // Here we tell for Equality Comparer to compare optimized expressions
            //
            builder.AddQueryableMemberAccessors(new AccessorMember(memberInfo), builder.DataContext, (mi, dc) =>
            {
                var filtered = (IQueryable)filterFunc.DynamicInvoke(fakeQuery, dc) !;

                // here we use light version of optimization, only for comparing trees
                var optimizationContext = new ExpressionTreeOptimizationContext(dc);
                var optimizedExpr       = optimizationContext.ExposeExpression(filtered.Expression);
                optimizedExpr           = optimizationContext.ExpandQueryableMethods(optimizedExpr);
                optimizedExpr           = optimizedExpr.OptimizeExpression() !;
                return(optimizedExpr);
            });

            var filtered  = (IQueryable)filterFunc.DynamicInvoke(fakeQuery, builder.DataContext) !;
            var optimized = filtered.Expression;

            optimized = builder.ConvertExpressionTree(optimized);
            optimized = builder.ConvertExpression(optimized);
            optimized = optimized.OptimizeExpression() !;

            var refExpression = new ContextRefExpression(typeof(IQueryable <>).MakeGenericType(entityType), tableContext);
            var replaced      = optimized.Replace(fakeQuery.Expression, refExpression);

            if (replaced == optimized)
            {
                throw new LinqException("Could not correct query result for processing.");
            }

            var context = builder.BuildSequence(new BuildInfo(buildInfo, replaced));

            return(context);
        }
コード例 #2
0
            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);
            }
コード例 #3
0
ファイル: MergeBuilder.On.cs プロジェクト: zabolotnev/linq2db
            protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
            {
                var mergeContext = (MergeContext)builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0]));

                var statement = mergeContext.Merge;

                if (methodCall.Arguments.Count == 2)
                {
                    // On<TTarget, TSource>(IMergeableOn<TTarget, TSource> merge, Expression<Func<TTarget, TSource, bool>> matchCondition)
                    var predicate     = methodCall.Arguments[1];
                    var condition     = (LambdaExpression)predicate.Unwrap();
                    var conditionExpr = builder.ConvertExpression(condition.Body.Unwrap());

                    mergeContext.AddTargetParameter(condition.Parameters[0]);
                    mergeContext.AddSourceParameter(condition.Parameters[1]);

                    builder.BuildSearchCondition(
                        new ExpressionContext(null, new[] { mergeContext.TargetContext, mergeContext.SourceContext }, condition),
                        conditionExpr,
                        statement.On.Conditions,
                        false);
                }
                else if (methodCall.Arguments.Count == 3)
                {
                    var targetKeyLambda = ((LambdaExpression)methodCall.Arguments[1].Unwrap());
                    var sourceKeyLambda = ((LambdaExpression)methodCall.Arguments[2].Unwrap());

                    var targetKeySelector = targetKeyLambda.Body.Unwrap();
                    var sourceKeySelector = sourceKeyLambda.Body.Unwrap();

                    var targetKeyContext = new ExpressionContext(buildInfo.Parent, mergeContext.TargetContext, targetKeyLambda);
                    var sourceKeyContext = new ExpressionContext(buildInfo.Parent, mergeContext.SourceContext, sourceKeyLambda);

                    if (targetKeySelector.NodeType == ExpressionType.New)
                    {
                        var new1 = (NewExpression)targetKeySelector;
                        var new2 = (NewExpression)sourceKeySelector;

                        for (var i = 0; i < new1.Arguments.Count; i++)
                        {
                            var arg1 = new1.Arguments[i];
                            var arg2 = new2.Arguments[i];

                            JoinBuilder.BuildJoin(builder, statement.On, targetKeyContext, arg1, sourceKeyContext, arg2);
                        }
                    }
                    else if (targetKeySelector.NodeType == ExpressionType.MemberInit)
                    {
                        // TODO: migrate unordered members support to original code
                        var mi1 = (MemberInitExpression)targetKeySelector;
                        var mi2 = (MemberInitExpression)sourceKeySelector;

                        if (mi1.Bindings.Count != mi2.Bindings.Count)
                        {
                            throw new LinqException($"List of member inits does not match for entity type '{targetKeySelector.Type}'.");
                        }

                        for (var i = 0; i < mi1.Bindings.Count; i++)
                        {
                            var binding2 = (MemberAssignment)mi2.Bindings.Where(b => b.Member == mi1.Bindings[i].Member).FirstOrDefault();
                            if (binding2 == null)
                            {
                                throw new LinqException($"List of member inits does not match for entity type '{targetKeySelector.Type}'.");
                            }

                            var arg1 = ((MemberAssignment)mi1.Bindings[i]).Expression;
                            var arg2 = binding2.Expression;

                            JoinBuilder.BuildJoin(builder, statement.On, targetKeyContext, arg1, sourceKeyContext, arg2);
                        }
                    }
                    else
                    {
                        JoinBuilder.BuildJoin(builder, statement.On, targetKeyContext, targetKeySelector, sourceKeyContext, sourceKeySelector);
                    }
                }
                else
                {
                    // OnTargetKey<TTarget>(IMergeableOn<TTarget, TTarget> merge)
                    var targetType       = methodCall.Method.GetGenericArguments()[0];
                    var pTarget          = Expression.Parameter(targetType, "t");
                    var pSource          = Expression.Parameter(targetType, "s");
                    var targetDescriptor = builder.MappingSchema.GetEntityDescriptor(targetType);

                    Expression?ex = null;

                    for (var i = 0; i < targetDescriptor.Columns.Count; i++)
                    {
                        var column = targetDescriptor.Columns[i];
                        if (!column.IsPrimaryKey)
                        {
                            continue;
                        }

                        var expr = Expression.Equal(
                            Expression.MakeMemberAccess(pTarget, column.MemberInfo),
                            Expression.MakeMemberAccess(pSource, column.MemberInfo));
                        ex = ex != null?Expression.AndAlso(ex, expr) : expr;
                    }

                    if (ex == null)
                    {
                        throw new LinqToDBException("Method OnTargetKey() needs at least one primary key column");
                    }

                    var condition = Expression.Lambda(ex, pTarget, pSource);

                    builder.BuildSearchCondition(
                        new ExpressionContext(null, new[] { mergeContext.TargetContext, mergeContext.SourceContext }, condition),
                        ex,
                        statement.On.Conditions,
                        false);
                }

                mergeContext.SourceContext.MatchBuilt();
                return(mergeContext);
            }
コード例 #4
0
ファイル: AssociationHelper.cs プロジェクト: zxd60/linq2db
        // Returns
        // (ParentType p) => dc.GetTable<ObjectType>().Where(...)
        // (ParentType p) => dc.GetTable<ObjectType>().Where(...).DefaultIfEmpty
        public static LambdaExpression CreateAssociationQueryLambda(ExpressionBuilder builder, AccessorMember onMember, AssociationDescriptor association,
                                                                    Type parentOriginalType,
                                                                    Type parentType,
                                                                    Type objectType, bool inline, bool enforceDefault,
                                                                    List <LoadWithInfo[]>?loadWith, out bool isLeft)
        {
            var dataContextConstant = Expression.Constant(builder.DataContext, builder.DataContext.GetType());

            // We are trying to keep fast cache hit behaviour, so cache check should be added only if needed
            //
            bool shouldAddCacheCheck = false;

            bool cacheCheckAdded = false;

            LambdaExpression?definedQueryMethod = null;

            if (association.HasQueryMethod())
            {
                // here we tell for Expression Comparer to compare optimized Association expressions
                //
                definedQueryMethod = (LambdaExpression)builder.AddQueryableMemberAccessors(onMember, builder.DataContext, (mi, dc) =>
                {
                    var queryLambda         = association.GetQueryMethod(parentType, objectType) ?? throw new InvalidOperationException();
                    var optimizationContext = new ExpressionTreeOptimizationContext(dc);
                    var optimizedExpr       = optimizationContext.ExposeExpression(queryLambda);
                    optimizedExpr           = optimizationContext.ExpandQueryableMethods(optimizedExpr);
                    optimizedExpr           = optimizedExpr.OptimizeExpression() !;
                    return(optimizedExpr);
                });

                cacheCheckAdded = true;

                var parameterMatch = new Dictionary <ParameterExpression, Expression>();
                if (onMember.Arguments == null)
                {
                    if (definedQueryMethod.Parameters.Count > 1 && typeof(IDataContext).IsSameOrParentOf(definedQueryMethod.Parameters[1].Type))
                    {
                        parameterMatch.Add(definedQueryMethod.Parameters[1], dataContextConstant);
                    }
                }
                else
                {
                    var definedCount   = definedQueryMethod.Parameters.Count;
                    var argumentsCount = onMember.Arguments.Count;
                    var diff           = definedCount - argumentsCount;
                    for (int i = definedCount - 1; i >= diff; i--)
                    {
                        parameterMatch.Add(definedQueryMethod.Parameters[i], onMember.Arguments[i - diff]);
                    }
                }

                var body = definedQueryMethod.Body.Transform(e =>
                {
                    if (e.NodeType == ExpressionType.Parameter &&
                        parameterMatch.TryGetValue((ParameterExpression)e, out var newExpression))
                    {
                        return(newExpression);
                    }

                    return(e);
                });

                definedQueryMethod = Expression.Lambda(body, definedQueryMethod.Parameters[0]);
            }

            var shouldAddDefaultIfEmpty = enforceDefault;

            if (definedQueryMethod == null)
            {
                var parentParam = Expression.Parameter(parentType, "parent");
                var childParam  = Expression.Parameter(objectType, association.AliasName);

                var parentAccessor = TypeAccessor.GetAccessor(parentType);
                var childAccessor  = TypeAccessor.GetAccessor(objectType);

                Expression?predicate = null;
                for (var i = 0; i < association.ThisKey.Length; i++)
                {
                    var parentName   = association.ThisKey[i];
                    var parentMember = parentAccessor.Members.Find(m => m.MemberInfo.Name == parentName);

                    if (parentMember == null)
                    {
                        throw new LinqException("Association key '{0}' not found for type '{1}.", parentName,
                                                parentType);
                    }

                    var childName   = association.OtherKey[i];
                    var childMember = childAccessor.Members.Find(m => m.MemberInfo.Name == childName);

                    if (childMember == null)
                    {
                        throw new LinqException("Association key '{0}' not found for type '{1}.", childName,
                                                objectType);
                    }

                    var current = ExpressionBuilder.Equal(builder.MappingSchema,
                                                          Expression.MakeMemberAccess(parentParam, parentMember.MemberInfo),
                                                          Expression.MakeMemberAccess(childParam, childMember.MemberInfo));

                    predicate = predicate == null ? current : Expression.AndAlso(predicate, current);
                }

                var expressionPredicate = association.GetPredicate(parentType, objectType);

                if (expressionPredicate != null)
                {
                    shouldAddDefaultIfEmpty = true;
                    shouldAddCacheCheck     = true;

                    var replacedBody = expressionPredicate.GetBody(parentParam, childParam);

                    predicate = predicate == null ? replacedBody : Expression.AndAlso(predicate, replacedBody);
                }

                if (predicate == null)
                {
                    throw new LinqException("Can not generate Association predicate");
                }

                if (inline && !shouldAddDefaultIfEmpty)
                {
                    var ed = builder.MappingSchema.GetEntityDescriptor(objectType);
                    if (ed.QueryFilterFunc != null)
                    {
                        shouldAddDefaultIfEmpty = true;
                        shouldAddCacheCheck     = true;
                    }
                }

                var queryParam = Expression.Call(Methods.LinqToDB.GetTable.MakeGenericMethod(objectType), dataContextConstant);

                var        filterLambda = Expression.Lambda(predicate, childParam);
                Expression body         = Expression.Call(Methods.Queryable.Where.MakeGenericMethod(objectType), queryParam,
                                                          filterLambda);

                definedQueryMethod = Expression.Lambda(body, parentParam);
            }
            else
            {
                shouldAddDefaultIfEmpty = true;
                var bodyExpression = definedQueryMethod.Body.Unwrap();
                if (bodyExpression.NodeType == ExpressionType.Call)
                {
                    var mc = (MethodCallExpression)bodyExpression;
                    if (mc.IsSameGenericMethod(Methods.Queryable.DefaultIfEmpty, Methods.Queryable.DefaultIfEmptyValue))
                    {
                        shouldAddDefaultIfEmpty = false;
                    }
                }
            }

            if (!cacheCheckAdded && shouldAddCacheCheck)
            {
                // here we tell for Expression Comparer to compare optimized Association expressions
                //
                var closureExpr = definedQueryMethod;
                definedQueryMethod = (LambdaExpression)builder.AddQueryableMemberAccessors(onMember, builder.DataContext, (mi, dc) =>
                {
                    var optimizationContext = new ExpressionTreeOptimizationContext(dc);
                    var optimizedExpr       = optimizationContext.ExposeExpression(closureExpr);
                    optimizedExpr           = optimizationContext.ExpandQueryableMethods(optimizedExpr);
                    optimizedExpr           = optimizedExpr.OptimizeExpression() !;
                    return(optimizedExpr);
                });
            }

            if (loadWith != null)
            {
                var associationLoadWith = GetLoadWith(loadWith)?
                                          .FirstOrDefault(li => li.Info.MemberInfo == association.MemberInfo);

                if (associationLoadWith != null &&
                    (associationLoadWith.Info.MemberFilter != null || associationLoadWith.Info.FilterFunc != null))
                {
                    var body = definedQueryMethod.Body.Unwrap();

                    var memberFilter = associationLoadWith.Info.MemberFilter;
                    if (memberFilter != null)
                    {
                        var elementType = EagerLoading.GetEnumerableElementType(memberFilter.Parameters[0].Type,
                                                                                builder.MappingSchema);
                        var filtered   = Expression.Convert(body, typeof(IEnumerable <>).MakeGenericType(elementType));
                        var filterBody = memberFilter.GetBody(filtered);
                        body = Expression.Call(
                            Methods.Enumerable.AsQueryable.MakeGenericMethod(objectType), filterBody);
                    }

                    var loadWithFunc = associationLoadWith.Info.FilterFunc;

                    if (loadWithFunc != null)
                    {
                        loadWithFunc = loadWithFunc.Unwrap();
                        if (loadWithFunc is LambdaExpression lambda)
                        {
                            body = lambda.GetBody(body);
                        }
                        else
                        {
                            var filterDelegate = loadWithFunc.EvaluateExpression <Delegate>() ??
                                                 throw new LinqException("Cannot convert filter function '{loadWithFunc}' to Delegate.");

                            var arumentType = filterDelegate.GetType().GetGenericArguments()[0].GetGenericArguments()[0];
                            // check for fake argument q => q
                            if (arumentType.IsSameOrParentOf(objectType))
                            {
                                var query    = ExpressionQueryImpl.CreateQuery(objectType, builder.DataContext, body);
                                var filtered = (IQueryable)filterDelegate.DynamicInvoke(query) !;
                                body = filtered.Expression;
                            }
                        }
                    }

                    definedQueryMethod = Expression.Lambda(body, definedQueryMethod.Parameters);
                }

                if (associationLoadWith?.NextLoadWith != null && associationLoadWith.NextLoadWith.Count > 0)
                {
                    definedQueryMethod = (LambdaExpression)EnrichTablesWithLoadWith(builder.DataContext, definedQueryMethod, objectType,
                                                                                    associationLoadWith.NextLoadWith, builder.MappingSchema);
                }
            }

            if (parentOriginalType != parentType)
            {
                // add discriminator filter
                var ed = builder.MappingSchema.GetEntityDescriptor(parentOriginalType);
                foreach (var inheritanceMapping in ed.InheritanceMapping)
                {
                    if (inheritanceMapping.Type == parentType)
                    {
                        var objParam     = Expression.Parameter(objectType, "o");
                        var filterLambda = Expression.Lambda(ExpressionBuilder.Equal(builder.MappingSchema,
                                                                                     Expression.MakeMemberAccess(definedQueryMethod.Parameters[0], inheritanceMapping.Discriminator.MemberInfo),
                                                                                     Expression.Constant(inheritanceMapping.Code)), objParam);

                        var body = definedQueryMethod.Body.Unwrap();
                        body = Expression.Call(Methods.Queryable.Where.MakeGenericMethod(objectType),
                                               body, filterLambda);
                        definedQueryMethod = Expression.Lambda(body, definedQueryMethod.Parameters);

                        shouldAddDefaultIfEmpty = true;
                        break;
                    }
                }
            }

            if (inline && shouldAddDefaultIfEmpty)
            {
                var body = definedQueryMethod.Body.Unwrap();
                body = Expression.Call(Methods.Queryable.DefaultIfEmpty.MakeGenericMethod(objectType), body);
                definedQueryMethod = Expression.Lambda(body, definedQueryMethod.Parameters);
                isLeft             = true;
            }
            else
            {
                isLeft = false;
            }

            definedQueryMethod = (LambdaExpression)builder.ConvertExpressionTree(definedQueryMethod);
            definedQueryMethod = (LambdaExpression)builder.ConvertExpression(definedQueryMethod);
            definedQueryMethod = (LambdaExpression)definedQueryMethod.OptimizeExpression() !;

            return(definedQueryMethod);
        }
コード例 #5
0
        protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
        {
            var outerContext = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0], buildInfo.SelectQuery));
            var innerContext = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[1], new SelectQuery()));

            outerContext = new SubQueryContext(outerContext);
            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);

            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 (conditionIndex != -1)
            {
                var condition     = (LambdaExpression)methodCall.Arguments[conditionIndex].Unwrap();
                var conditionExpr = builder.ConvertExpression(condition.Body.Unwrap());

                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(
                    new ExpressionContext(null, new[] { outerContext, innerContext }, condition),
                    conditionExpr,
                    join.JoinedTable.Condition.Conditions,
                    false);
            }
            else
            {
                outerContext.SelectQuery.From.Table(innerContext.SelectQuery);
            }

            return(new SelectContext(buildInfo.Parent, selector, outerContext, innerContext)
#if DEBUG
            {
                MethodCall = methodCall
            }
#endif
                   );
        }
コード例 #6
0
ファイル: LoadWithBuilder.cs プロジェクト: X10sions/linq2db
        static IEnumerable <MemberInfo> GetAssociations(ExpressionBuilder builder, Expression expression, Expression?stopExpression)
        {
            MemberInfo?lastMember = null;

            for (;;)
            {
                if (stopExpression == expression)
                {
                    yield break;
                }

                switch (expression.NodeType)
                {
                case ExpressionType.Parameter:
                    if (lastMember == null)
                    {
                        goto default;
                    }
                    yield break;

                case ExpressionType.Call:
                {
                    var cexpr = (MethodCallExpression)expression;

                    if (cexpr.Method.IsSqlPropertyMethodEx())
                    {
                        foreach (var assoc in GetAssociations(builder, builder.ConvertExpression(expression), stopExpression))
                        {
                            yield return(assoc);
                        }

                        yield break;
                    }

                    if (lastMember == null)
                    {
                        goto default;
                    }

                    var expr = cexpr.Object;

                    if (expr == null)
                    {
                        if (cexpr.Arguments.Count == 0)
                        {
                            goto default;
                        }

                        expr = cexpr.Arguments[0];
                    }

                    if (expr.NodeType != ExpressionType.MemberAccess)
                    {
                        goto default;
                    }

                    var member = ((MemberExpression)expr).Member;
                    var mtype  = member.GetMemberType();

                    if (lastMember.ReflectedType != mtype.GetItemType())
                    {
                        goto default;
                    }

                    expression = expr;

                    break;
                }

                case ExpressionType.MemberAccess:
                {
                    var mexpr  = (MemberExpression)expression;
                    var member = lastMember = mexpr.Member;
                    var attr   = builder.MappingSchema.GetAttribute <AssociationAttribute>(member.ReflectedType !, member);
                    if (attr == null)
                    {
                        member = mexpr.Expression.Type.GetMemberEx(member) !;
                        attr   = builder.MappingSchema.GetAttribute <AssociationAttribute>(mexpr.Expression.Type, member);
                    }
                    if (attr == null)
                    {
                        throw new LinqToDBException($"Member '{expression}' is not an association.");
                    }

                    yield return(member);

                    expression = mexpr.Expression;

                    break;
                }

                case ExpressionType.ArrayIndex:
                {
                    expression = ((BinaryExpression)expression).Left;
                    break;
                }

                case ExpressionType.Extension:
                {
                    if (expression is GetItemExpression getItemExpression)
                    {
                        expression = getItemExpression.Expression;
                        break;
                    }

                    goto default;
                }

                case ExpressionType.Convert:
                {
                    expression = ((UnaryExpression)expression).Operand;
                    break;
                }

                default:
                {
                    throw new LinqToDBException($"Expression '{expression}' is not an association.");
                }
                }
            }
        }
コード例 #7
0
        static IBuildContext BuildCteContext(ExpressionBuilder builder, BuildInfo buildInfo)
        {
            var methodCall = (MethodCallExpression)buildInfo.Expression;

            Expression bodyExpr;
            IQueryable?query       = null;
            string?    name        = null;
            bool       isRecursive = false;

            switch (methodCall.Arguments.Count)
            {
            case 1:
                bodyExpr = methodCall.Arguments[0].Unwrap();
                break;

            case 2:
                bodyExpr = methodCall.Arguments[0].Unwrap();
                name     = methodCall.Arguments[1].EvaluateExpression() as string;
                break;

            case 3:
                query       = methodCall.Arguments[0].EvaluateExpression() as IQueryable;
                bodyExpr    = methodCall.Arguments[1].Unwrap();
                name        = methodCall.Arguments[2].EvaluateExpression() as string;
                isRecursive = true;
                break;

            default:
                throw new InvalidOperationException();
            }

            bodyExpr = builder.ConvertExpression(bodyExpr);
            builder.RegisterCte(query, bodyExpr, () => new CteClause(null, bodyExpr.Type.GetGenericArguments()[0], isRecursive, name));

            var cte = builder.BuildCte(bodyExpr,
                                       cteClause =>
            {
                var info     = new BuildInfo(buildInfo, bodyExpr, new SelectQuery());
                var sequence = builder.BuildSequence(info);

                if (cteClause == null)
                {
                    cteClause = new CteClause(sequence.SelectQuery, bodyExpr.Type.GetGenericArguments()[0], isRecursive, name);
                }
                else
                {
                    cteClause.Body = sequence.SelectQuery;
                    cteClause.Name = name;
                }

                return(Tuple.Create(cteClause, (IBuildContext?)sequence));
            }
                                       );

            var cteBuildInfo = new BuildInfo(buildInfo, bodyExpr, buildInfo.SelectQuery);
            var cteContext   = new CteTableContext(builder, cteBuildInfo, cte.Item1, bodyExpr);

            // populate all fields
            if (isRecursive)
            {
                cteContext.ConvertToSql(null, 0, ConvertFlags.All);
            }

            return(cteContext);
        }
コード例 #8
0
        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;

                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);

                builder.BuildSearchCondition(
                    joinContext,
                    conditionExpr,
                    @join.JoinedTable.Condition.Conditions);
            }
            else
            {
                outerContext.SelectQuery.From.Table(innerContext.SelectQuery);
            }

            return(joinContext);
        }
コード例 #9
0
            protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
            {
                var mergeContext = (MergeContext)builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0]));

                var statement = mergeContext.Merge;
                var operation = new SqlMergeOperationClause(MergeOperationType.Insert);

                statement.Operations.Add(operation);

                var predicate = methodCall.Arguments[1];
                var setter    = methodCall.Arguments[2];

                if (!setter.IsNullValue())
                {
                    var setterExpression = (LambdaExpression)setter.Unwrap();
                    mergeContext.AddSourceParameter(setterExpression.Parameters[0]);

                    UpdateBuilder.BuildSetterWithContext(
                        builder,
                        buildInfo,
                        setterExpression,
                        mergeContext.TargetContext,
                        operation.Items,
                        mergeContext.SourceContext);
                }
                else
                {
                    // build setters like QueryRunner.Insert
                    var sqlTable = (SqlTable)statement.Target.Source;
                    var param    = Expression.Parameter(sqlTable.ObjectType, "s");

                    foreach (var field in sqlTable.Fields)
                    {
                        if (field.IsInsertable)
                        {
                            var expression = LinqToDB.Expressions.Extensions.GetMemberGetter(field.ColumnDescriptor.MemberInfo, param);
                            var tgtExpr    = mergeContext.TargetContext.ConvertToSql(builder.ConvertExpression(expression), 1, ConvertFlags.Field)[0].Sql;
                            var srcExpr    = mergeContext.SourceContext.ConvertToSql(builder.ConvertExpression(expression), 1, ConvertFlags.Field)[0].Sql;

                            operation.Items.Add(new SqlSetExpression(tgtExpr, srcExpr));
                        }
                        else if (field.IsIdentity)
                        {
                            var expr = builder.DataContext.CreateSqlProvider().GetIdentityExpression(sqlTable);

                            if (expr != null)
                            {
                                operation.Items.Add(new SqlSetExpression(field, expr));
                            }
                        }
                    }
                }

                if (!predicate.IsNullValue())
                {
                    var condition     = (LambdaExpression)predicate.Unwrap();
                    var conditionExpr = builder.ConvertExpression(condition.Body.Unwrap());

                    operation.Where = new SqlSearchCondition();

                    builder.BuildSearchCondition(
                        new ExpressionContext(null, new[] { mergeContext.SourceContext }, condition),
                        conditionExpr,
                        operation.Where.Conditions);
                }

                return(mergeContext);
            }