Example #1
0
            static Expression MakeSubQueryExpression(MappingSchema mappingSchema, Expression sequence,
                                                     ParameterExpression param, Expression expr1, Expression expr2)
            {
                var filterLambda = Expression.Lambda(ExpressionBuilder.Equal(mappingSchema, expr1, expr2), param);

                return(TypeHelper.MakeMethodCall(Methods.Enumerable.Where, sequence, filterLambda));
            }
Example #2
0
				public Expression GetGroupJoin(GroupJoinContext context)
				{
					// Convert outer condition.
					//
					var outerParam = Expression.Parameter(context._outerKeyLambda.Body.Type, "o");
					var outerKey   = context._outerKeyLambda.GetBody(context.Lambda.Parameters[0]);

					outerKey = context.Builder.BuildExpression(context, outerKey, false);

					// Convert inner condition.
					//
					var parameters = context.Builder.CurrentSqlParameters
						.Select((p,i) => new { p, i })
						.ToDictionary(_ => _.p.Expression, _ => _.i);
					var paramArray = Expression.Parameter(typeof(object[]), "ps");

					var innerKey = context._innerKeyLambda.Body.Transform(e =>
					{
						int idx;

						if (parameters.TryGetValue(e, out idx))
						{
							return Expression.Convert(
								Expression.ArrayIndex(paramArray, Expression.Constant(idx)),
								e.Type);
						}

						return e;
					});

					// Item reader.
					//
					var expr = Expression.Call(
						null,
						MemberHelper.MethodOf(() => Queryable.Where(null, (Expression<Func<TElement,bool>>)null)),
						context._innerExpression,
						Expression.Lambda<Func<TElement,bool>>(
							ExpressionBuilder.Equal(context.Builder.MappingSchema, innerKey, outerParam),
							new[] { context._innerKeyLambda.Parameters[0] }));

					var lambda = Expression.Lambda<Func<IDataContext,TKey,object[],IQueryable<TElement>>>(
						Expression.Convert(expr, typeof(IQueryable<TElement>)),
						Expression.Parameter(typeof(IDataContext), "ctx"),
						outerParam,
						paramArray);

					var itemReader = CompiledQuery.Compile(lambda);

					return Expression.Call(
						null,
						MemberHelper.MethodOf(() => GetGrouping(null, null, default(TKey), null)),
						new[]
						{
							ExpressionBuilder.QueryRunnerParam,
							Expression.Constant(context.Builder.CurrentSqlParameters),
							outerKey,
							Expression.Constant(itemReader),
						});
				}
Example #3
0
                public Expression GetContext(MappingSchema mappingSchema, Expression sequence, ParameterExpression param, Expression expr1, Expression expr2)
                {
// ReSharper disable AssignNullToNotNullAttribute
                    //ReflectionHelper.Expressor<object>.MethodExpressor(_ => Queryable.Where(null, (Expression<Func<T,bool>>)null)),
                    var mi = MemberHelper.MethodOf(() => Enumerable.Where(null, (Func <T, bool>)null));
// ReSharper restore AssignNullToNotNullAttribute
                    var arg2 = Expression.Lambda <Func <T, bool> >(ExpressionBuilder.Equal(mappingSchema, expr1, expr2), new[] { param });

                    return(Expression.Call(null, mi, sequence, arg2));
                }
Example #4
0
				public Expression GetGroupJoinCall(GroupJoinContext context)
				{
					var expr = Expression.Call(
						null,
						MemberHelper.MethodOf(() => Queryable.Where(null, (Expression<Func<T,bool>>)null)),
						context._innerExpression,
						Expression.Lambda<Func<T,bool>>(
							ExpressionBuilder.Equal(
								context.Builder.MappingSchema,
								context._innerKeyLambda.Body.Unwrap(),
								context._outerKeyLambda.GetBody(context.Lambda.Parameters[0])),
							new[] { context._innerKeyLambda.Parameters[0] }));

					return expr;
				}
Example #5
0
            public override ISqlExpression GetSubQuery(IBuildContext context)
            {
                if (_subQuerySql == null)
                {
                    var args      = _methodCall.Method.GetGenericArguments();
                    var param     = Expression.Parameter(args[0], "param");
                    var expr      = _methodCall.Arguments[1];
                    var condition = Expression.Lambda(ExpressionBuilder.Equal(Builder.MappingSchema, param, expr), param);

                    IBuildContext ctx = new ExpressionContext(Parent, Sequence, condition);

                    ctx = Builder.GetContext(ctx, expr) ?? ctx;

                    Builder.ReplaceParent(ctx, this);

                    SelectQuery.Condition cond;

                    if (Sequence.SelectQuery != SelectQuery &&
                        (ctx.IsExpression(expr, 0, RequestFor.Field).Result ||
                         ctx.IsExpression(expr, 0, RequestFor.Expression).Result))
                    {
                        Sequence.ConvertToIndex(null, 0, ConvertFlags.All);
                        var ex = Builder.ConvertToSql(ctx, _methodCall.Arguments[1]);
                        cond = new SelectQuery.Condition(false, new SelectQuery.Predicate.InSubQuery(ex, false, SelectQuery));
                    }
                    else
                    {
                        var sequence = Builder.BuildWhere(Parent, Sequence, condition, true);
                        cond = new SelectQuery.Condition(false, new SelectQuery.Predicate.FuncLike(SqlFunction.CreateExists(sequence.SelectQuery)));
                    }

                    _subQuerySql = new SelectQuery.SearchCondition(cond);
                }

                return(_subQuerySql);
            }
Example #6
0
                public Expression GetGrouping(GroupByContext context)
                {
                    if (Configuration.Linq.GuardGrouping)
                    {
                        if (context._element.Lambda.Parameters.Count == 1 &&
                            context._element.Body == context._element.Lambda.Parameters[0])
                        {
                            var ex = new LinqToDBException(
                                "You should explicitly specify selected fields for server-side GroupBy() call or add AsEnumerable() call before GroupBy() to perform client-side grouping.\n" +
                                "Set Configuration.Linq.GuardGrouping = false to disable this check."
                                )
                            {
                                HelpLink = "https://github.com/linq2db/linq2db/issues/365"
                            };

                            throw ex;
                        }
                    }

                    var parameters = context.Builder.CurrentSqlParameters
                                     .Select((p, i) => new { p, i })
                                     .ToDictionary(_ => _.p.Expression, _ => _.i);
                    var paramArray = Expression.Parameter(typeof(object[]), "ps");

                    var groupExpression = context._sequenceExpr.Transform(e =>
                    {
                        int idx;

                        if (parameters.TryGetValue(e, out idx))
                        {
                            return
                            (Expression.Convert(
                                 Expression.ArrayIndex(paramArray, Expression.Constant(idx)),
                                 e.Type));
                        }

                        return(e);
                    });

                    var keyParam = Expression.Parameter(typeof(TKey), "key");

// ReSharper disable AssignNullToNotNullAttribute

                    var expr = Expression.Call(
                        null,
                        MemberHelper.MethodOf(() => Queryable.Where(null, (Expression <Func <TSource, bool> >)null)),
                        groupExpression,
                        Expression.Lambda <Func <TSource, bool> >(
                            ExpressionBuilder.Equal(context.Builder.MappingSchema, context._key.Lambda.Body, keyParam),
                            new[] { context._key.Lambda.Parameters[0] }));

                    expr = Expression.Call(
                        null,
                        MemberHelper.MethodOf(() => Queryable.Select(null, (Expression <Func <TSource, TElement> >)null)),
                        expr,
                        context._element.Lambda);

// ReSharper restore AssignNullToNotNullAttribute

                    var lambda = Expression.Lambda <Func <IDataContext, TKey, object[], IQueryable <TElement> > >(
                        Expression.Convert(expr, typeof(IQueryable <TElement>)),
                        Expression.Parameter(typeof(IDataContext), "ctx"),
                        keyParam,
                        paramArray);

                    var itemReader      = CompiledQuery.Compile(lambda);
                    var keyExpr         = context._key.BuildExpression(null, 0, false);
                    var dataReaderLocal = context.Builder.DataReaderLocal;

                    if (!Configuration.AvoidSpecificDataProviderAPI && keyExpr.Find(e => e == dataReaderLocal) != null)
                    {
                        keyExpr = Expression.Block(
                            new[] { context.Builder.DataReaderLocal },
                            new[]
                        {
                            Expression.Assign(dataReaderLocal, Expression.Convert(ExpressionBuilder.DataReaderParam, context.Builder.DataContext.DataReaderType)),
                            keyExpr
                        });
                    }

                    var keyReader = Expression.Lambda <Func <IQueryRunner, IDataContext, IDataReader, Expression, object[], TKey> >(
                        keyExpr,
                        new []
                    {
                        ExpressionBuilder.QueryRunnerParam,
                        ExpressionBuilder.DataContextParam,
                        ExpressionBuilder.DataReaderParam,
                        ExpressionBuilder.ExpressionParam,
                        ExpressionBuilder.ParametersParam
                    });

                    return(Expression.Call(
                               null,
                               MemberHelper.MethodOf(() => GetGrouping(null, null, null, null, null, null, null, null)),
                               new Expression[]
                    {
                        ExpressionBuilder.QueryRunnerParam,
                        ExpressionBuilder.DataContextParam,
                        ExpressionBuilder.DataReaderParam,
                        Expression.Constant(context.Builder.CurrentSqlParameters),
                        ExpressionBuilder.ExpressionParam,
                        ExpressionBuilder.ParametersParam,
                        Expression.Constant(keyReader.Compile()),
                        Expression.Constant(itemReader)
                    }));
                }
Example #7
0
        // 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);
        }
Example #8
0
                public Expression GetGrouping(GroupByContext context)
                {
                    if (Configuration.Linq.GuardGrouping && !context._isGroupingGuardDisabled)
                    {
                        if (context.Element.Lambda.Parameters.Count == 1 &&
                            context.Element.Body == context.Element.Lambda.Parameters[0])
                        {
                            var ex = new LinqToDBException(
                                "You should explicitly specify selected fields for server-side GroupBy() call or add AsEnumerable() call before GroupBy() to perform client-side grouping.\n" +
                                "Set Configuration.Linq.GuardGrouping = false to disable this check.\n" +
                                "Additionally this guard exception can be disabled by extension GroupBy(...).DisableGuard().\n" +
                                "NOTE! By disabling this guard you accept additional Database Connection(s) to the server for processing such requests."
                                )
                            {
                                HelpLink = "https://github.com/linq2db/linq2db/issues/365"
                            };

                            throw ex;
                        }
                    }

                    var parameters = context.Builder.CurrentSqlParameters
                                     .Select((p, i) => new { p, i })
                                     .ToDictionary(_ => _.p.Expression, _ => _.i);
                    var paramArray = Expression.Parameter(typeof(object[]), "ps");

                    var groupExpression = context._sequenceExpr.Transform(e =>
                    {
                        if (parameters.TryGetValue(e, out var idx))
                        {
                            return
                            (Expression.Convert(
                                 Expression.ArrayIndex(paramArray, Expression.Constant(idx)),
                                 e.Type));
                        }

                        return(e);
                    });

                    var keyParam = Expression.Parameter(typeof(TKey), "key");

// ReSharper disable AssignNullToNotNullAttribute

                    var expr = Expression.Call(
                        null,
                        MemberHelper.MethodOf(() => Queryable.Where(null, (Expression <Func <TSource, bool> >)null !)),
                        groupExpression,
                        Expression.Lambda <Func <TSource, bool> >(
                            ExpressionBuilder.Equal(context.Builder.MappingSchema, context._key.Lambda.Body, keyParam),
                            new[] { context._key.Lambda.Parameters[0] }));

                    expr = Expression.Call(
                        null,
                        MemberHelper.MethodOf(() => Queryable.Select(null, (Expression <Func <TSource, TElement> >)null !)),
                        expr,
                        context.Element.Lambda);

// ReSharper restore AssignNullToNotNullAttribute

                    var lambda = Expression.Lambda <Func <IDataContext, TKey, object?[]?, IQueryable <TElement> > >(
                        Expression.Convert(expr, typeof(IQueryable <TElement>)),
                        Expression.Parameter(typeof(IDataContext), "ctx"),
                        keyParam,
                        paramArray);

                    var itemReader = CompiledQuery.Compile(lambda);
                    var keyExpr    = context._key.BuildExpression(null, 0, false);

                    var keyReader = Expression.Lambda <Func <IQueryRunner, IDataContext, IDataReader, Expression, object?[]?, TKey> >(
                        keyExpr,
                        new []
                    {
                        ExpressionBuilder.QueryRunnerParam,
                        ExpressionBuilder.DataContextParam,
                        ExpressionBuilder.DataReaderParam,
                        ExpressionBuilder.ExpressionParam,
                        ExpressionBuilder.ParametersParam
                    });

                    return(Expression.Call(
                               null,
                               MemberHelper.MethodOf(() => GetGrouping(null !, null !, null !, null !, null !, null !, null !, null !)),
                               new Expression[]
                    {
                        ExpressionBuilder.QueryRunnerParam,
                        ExpressionBuilder.DataContextParam,
                        ExpressionBuilder.DataReaderParam,
                        Expression.Constant(context.Builder.CurrentSqlParameters),
                        ExpressionBuilder.ExpressionParam,
                        ExpressionBuilder.ParametersParam,
                        keyReader,
                        Expression.Constant(itemReader)
                    }));
                }
                public Expression GetSubquery(
                    ExpressionBuilder builder,
                    AssociatedTableContext tableContext,
                    ParameterExpression parentObject)
                {
                    var lContext = Expression.Parameter(typeof(IDataContext), "ctx");
                    var lParent  = Expression.Parameter(typeof(object), "parentObject");

                    Expression expression;

                    var queryMethod = tableContext.Association.GetQueryMethod(parentObject.Type, typeof(T));

                    if (queryMethod != null)
                    {
                        var ownerParam = queryMethod.Parameters[0];
                        var dcParam    = queryMethod.Parameters[1];
                        var ownerExpr  = Expression.Convert(lParent, parentObject.Type);

                        expression = queryMethod.Body.Transform(e =>
                                                                e == ownerParam ? ownerExpr : (e == dcParam ? lContext : e));
                    }
                    else
                    {
                        var tableExpression = builder.DataContext.GetTable <T>();

                        var loadWith = tableContext.GetLoadWith();

                        if (loadWith != null)
                        {
                            foreach (var members in loadWith)
                            {
                                var pLoadWith  = Expression.Parameter(typeof(T), "t");
                                var isPrevList = false;

                                Expression obj = pLoadWith;

                                foreach (var member in members)
                                {
                                    if (isPrevList)
                                    {
                                        obj = new GetItemExpression(obj);
                                    }

                                    obj = Expression.MakeMemberAccess(obj, member);

                                    isPrevList = typeof(IEnumerable).IsSameOrParentOf(obj.Type);
                                }

                                tableExpression = tableExpression.LoadWith(Expression.Lambda <Func <T, object> >(obj, pLoadWith));
                            }
                        }

                        // Where
                        var pWhere = Expression.Parameter(typeof(T), "t");

                        Expression expr = null;

                        for (var i = 0; i < tableContext.Association.ThisKey.Length; i++)
                        {
                            var thisProp  = Expression.PropertyOrField(Expression.Convert(lParent, parentObject.Type), tableContext.Association.ThisKey[i]);
                            var otherProp = Expression.PropertyOrField(pWhere, tableContext.Association.OtherKey[i]);

                            var ex = ExpressionBuilder.Equal(tableContext.Builder.MappingSchema, otherProp, thisProp);

                            expr = expr == null ? ex : Expression.AndAlso(expr, ex);
                        }

                        var predicate = tableContext.Association.GetPredicate(parentObject.Type, typeof(T));
                        if (predicate != null)
                        {
                            var ownerParam = predicate.Parameters[0];
                            var childParam = predicate.Parameters[1];
                            var ownerExpr  = Expression.Convert(lParent, parentObject.Type);

                            var body = predicate.Body.Transform(e =>
                                                                e == ownerParam ? ownerExpr : (e == childParam ? pWhere : e));

                            expr = expr == null ? body : Expression.AndAlso(expr, body);
                        }

                        expression = tableExpression.Where(Expression.Lambda <Func <T, bool> >(expr, pWhere)).Expression;
                    }

                    var lambda      = Expression.Lambda <Func <IDataContext, object, IEnumerable <T> > >(expression, lContext, lParent);
                    var queryReader = CompiledQuery.Compile(lambda);

                    expression = Expression.Call(
                        null,
                        MemberHelper.MethodOf(() => ExecuteSubQuery(null, null, null)),
                        ExpressionBuilder.QueryRunnerParam,
                        Expression.Convert(parentObject, typeof(object)),
                        Expression.Constant(queryReader));

                    var memberType = tableContext.Association.MemberInfo.GetMemberType();

                    if (memberType == typeof(T[]))
                    {
                        return(Expression.Call(null, MemberHelper.MethodOf(() => Enumerable.ToArray <T>(null)), expression));
                    }

                    if (memberType.IsSameOrParentOf(typeof(List <T>)))
                    {
                        return(Expression.Call(null, MemberHelper.MethodOf(() => Enumerable.ToList <T>(null)), expression));
                    }

                    var ctor = memberType.GetConstructorEx(new[] { typeof(IEnumerable <T>) });

                    if (ctor != null)
                    {
                        return(Expression.New(ctor, expression));
                    }

                    var l = builder.MappingSchema.GetConvertExpression(expression.Type, memberType, false, false);

                    if (l != null)
                    {
                        return(l.GetBody(expression));
                    }

                    throw new LinqToDBException($"Expected constructor '{memberType.Name}(IEnumerable<{tableContext.ObjectType}>)'");
                }
Example #10
0
                public Expression GetGrouping(GroupByContext context)
                {
                    var parameters = context.Builder.CurrentSqlParameters
                                     .Select((p, i) => new { p, i })
                                     .ToDictionary(_ => _.p.Expression, _ => _.i);
                    var paramArray = Expression.Parameter(typeof(object[]), "ps");

                    var groupExpression = context._sequenceExpr.Transform(e =>
                    {
                        int idx;

                        if (parameters.TryGetValue(e, out idx))
                        {
                            return
                            (Expression.Convert(
                                 Expression.ArrayIndex(paramArray, Expression.Constant(idx)),
                                 e.Type));
                        }

                        return(e);
                    });

                    var keyParam = Expression.Parameter(typeof(TKey), "key");

// ReSharper disable AssignNullToNotNullAttribute

                    var expr = Expression.Call(
                        null,
                        MemberHelper.MethodOf(() => Queryable.Where(null, (Expression <Func <TSource, bool> >)null)),
                        groupExpression,
                        Expression.Lambda <Func <TSource, bool> >(
                            ExpressionBuilder.Equal(context.Builder.MappingSchema, context._key.Lambda.Body, keyParam),
                            new[] { context._key.Lambda.Parameters[0] }));

                    expr = Expression.Call(
                        null,
                        MemberHelper.MethodOf(() => Queryable.Select(null, (Expression <Func <TSource, TElement> >)null)),
                        expr,
                        context._element.Lambda);

// ReSharper restore AssignNullToNotNullAttribute

                    var lambda = Expression.Lambda <Func <IDataContext, TKey, object[], IQueryable <TElement> > >(
                        Expression.Convert(expr, typeof(IQueryable <TElement>)),
                        Expression.Parameter(typeof(IDataContext), "ctx"),
                        keyParam,
                        paramArray);

                    var itemReader      = CompiledQuery.Compile(lambda);
                    var keyExpr         = context._key.BuildExpression(null, 0);
                    var dataReaderLocal = context.Builder.DataReaderLocal;

                    if (!Configuration.AvoidSpecificDataProviderAPI && keyExpr.Find(e => e == dataReaderLocal) != null)
                    {
                        keyExpr = Expression.Block(
                            new[] { context.Builder.DataReaderLocal },
                            new[]
                        {
                            Expression.Assign(dataReaderLocal, Expression.Convert(ExpressionBuilder.DataReaderParam, context.Builder.DataContextInfo.DataContext.DataReaderType)),
                            keyExpr
                        });
                    }

                    var keyReader = Expression.Lambda <Func <QueryContext, IDataContext, IDataReader, Expression, object[], TKey> >(
                        keyExpr,
                        new []
                    {
                        ExpressionBuilder.ContextParam,
                        ExpressionBuilder.DataContextParam,
                        ExpressionBuilder.DataReaderParam,
                        ExpressionBuilder.ExpressionParam,
                        ExpressionBuilder.ParametersParam
                    });

                    return(Expression.Call(
                               null,
                               MemberHelper.MethodOf(() => GetGrouping(null, null, null, null, null, null, null, null)),
                               new Expression[]
                    {
                        ExpressionBuilder.ContextParam,
                        ExpressionBuilder.DataContextParam,
                        ExpressionBuilder.DataReaderParam,
                        Expression.Constant(context.Builder.CurrentSqlParameters),
                        ExpressionBuilder.ExpressionParam,
                        ExpressionBuilder.ParametersParam,
                        Expression.Constant(keyReader.Compile()),
                        Expression.Constant(itemReader)
                    }));
                }