private Expression ExecuteProjection(DbProjectionExpression projection, bool okayToDefer, QueryCommand command, Expression[] values) { okayToDefer &= (this.receivingMember != null && this.policy.IsDeferLoaded(this.receivingMember)); var saveScope = this.scope; ParameterExpression reader = Expression.Parameter(typeof(IDataReader), "r" + nReaders++); this.scope = new Scope(this.scope, reader, projection.Select.Alias, projection.Select.Columns); LambdaExpression projector = Expression.Lambda(this.Visit(projection.Projector), reader); this.scope = saveScope; string methExecute = okayToDefer ? "ExecuteDeferred" : "Execute"; // call low-level execute directly on supplied DbQueryProvider Expression result = Expression.Call(this.provider, methExecute, new Type[] { projector.Body.Type }, Expression.Constant(command), projector, Expression.NewArrayInit(typeof(object), values) ); if (projection.Aggregator != null) { // apply aggregator result = DbExpressionReplacer.Replace(projection.Aggregator.Body, projection.Aggregator.Parameters[0], result); } return(result); }
protected override Expression VisitProjection(DbProjectionExpression proj) { if (proj.Select.Skip != null) { Expression newTake = (proj.Select.Take != null) ? Expression.Add(proj.Select.Skip, proj.Select.Take) : null; if (newTake != null) { newTake = PartialEvaluator.Eval(newTake); } var newSelect = proj.Select.SetSkip(null).SetTake(newTake); var elementType = proj.Type.GetSequenceElementType(); var agg = proj.Aggregator; var p = agg != null ? agg.Parameters[0] : Expression.Parameter(elementType, "p"); var skip = Expression.Call(typeof(Enumerable), "Skip", new Type[] { elementType }, p, proj.Select.Skip); if (agg != null) { agg = (LambdaExpression)DbExpressionReplacer.Replace(agg, p, skip); } else { agg = Expression.Lambda(skip, p); } return(new DbProjectionExpression(newSelect, proj.Projector, agg)); } return(proj); }
protected override Expression VisitClientJoin(DbClientJoinExpression join) { // convert client join into a up-front lookup table builder & replace client-join in tree with lookup accessor // 1) lookup = query.Select(e => new KVP(key: inner, value: e)).ToLookup(kvp => kvp.Key, kvp => kvp.Value) Expression innerKey = MakeJoinKey(join.InnerKey); Expression outerKey = MakeJoinKey(join.OuterKey); ConstructorInfo kvpConstructor = typeof(KeyValuePair <,>).MakeGenericType(innerKey.Type, join.Projection.Projector.Type).GetConstructor(new Type[] { innerKey.Type, join.Projection.Projector.Type }); Expression constructKVPair = Expression.New(kvpConstructor, innerKey, join.Projection.Projector); DbProjectionExpression newProjection = new DbProjectionExpression(join.Projection.Select, constructKVPair); int iLookup = ++nLookup; Expression execution = this.ExecuteProjection(newProjection, false); ParameterExpression kvp = Expression.Parameter(constructKVPair.Type, "kvp"); // filter out nulls if (join.Projection.Projector.NodeType == (ExpressionType)DbExpressionType.OuterJoined) { LambdaExpression pred = Expression.Lambda( Expression.PropertyOrField(kvp, "Value").NotEqual(TypeHelper.GetNullConstant(join.Projection.Projector.Type)), kvp ); execution = Expression.Call(typeof(Enumerable), "Where", new Type[] { kvp.Type }, execution, pred); } // make lookup LambdaExpression keySelector = Expression.Lambda(Expression.PropertyOrField(kvp, "Key"), kvp); LambdaExpression elementSelector = Expression.Lambda(Expression.PropertyOrField(kvp, "Value"), kvp); Expression toLookup = Expression.Call(typeof(Enumerable), "ToLookup", new Type[] { kvp.Type, outerKey.Type, join.Projection.Projector.Type }, execution, keySelector, elementSelector); // 2) agg(lookup[outer]) ParameterExpression lookup = Expression.Parameter(toLookup.Type, "lookup" + iLookup); PropertyInfo property = lookup.Type.GetProperty("Item"); Expression access = Expression.Call(lookup, property.GetGetMethod(), this.Visit(outerKey)); if (join.Projection.Aggregator != null) { // apply aggregator access = DbExpressionReplacer.Replace(join.Projection.Aggregator.Body, join.Projection.Aggregator.Parameters[0], access); } this.variables.Add(lookup); this.initializers.Add(toLookup); return(access); }