private Expression BindAggregate(Expression source, MethodInfo method, LambdaExpression argument, bool isRoot) { Type returnType = method.ReturnType; AggregateType aggType = this.GetAggregateType(method.Name); bool hasPredicateArg = this.HasPredicateArg(aggType); bool argumentWasPredicate = false; if (argument != null && hasPredicateArg) { // convert query.Count(predicate) into query.Where(predicate).Count() source = Expression.Call(typeof(Queryable), "Where", method.GetGenericArguments(), source, argument); argument = null; argumentWasPredicate = true; } ProjectionExpression projection = this.VisitSequence(source); Expression argExpr = null; if (argument != null) { this.map[argument.Parameters[0]] = projection.Projector; argExpr = this.Visit(argument.Body); } else if (!hasPredicateArg) { argExpr = projection.Projector; } string alias = this.GetNextAlias(); var pc = this.ProjectColumns(projection.Projector, alias, projection.Source.Alias); Expression aggExpr = new AggregateExpression(returnType, aggType, argExpr); Type selectType = typeof(IEnumerable <>).MakeGenericType(returnType); SelectExpression select = new SelectExpression(selectType, alias, new ColumnDeclaration[] { new ColumnDeclaration("", aggExpr) }, projection.Source, null); if (isRoot) { ParameterExpression p = Expression.Parameter(selectType, "p"); LambdaExpression gator = Expression.Lambda(Expression.Call(typeof(Enumerable), "Single", new Type[] { returnType }, p), p); return(new ProjectionExpression(select, new ColumnExpression(returnType, alias, ""), gator)); } SubqueryExpression subquery = new SubqueryExpression(returnType, select); // if we can find the corresponding group-info we can build a special AggregateSubquery node that will enable us to // optimize the aggregate expression later using AggregateRewriter GroupByInfo info; if (!argumentWasPredicate && this.groupByMap.TryGetValue(projection, out info)) { // use the element expression from the group-by info to rebind the argument so the resulting expression is one that // would be legal to add to the columns in the select expression that has the corresponding group-by clause. if (argument != null) { this.map[argument.Parameters[0]] = info.Element; argExpr = this.Visit(argument.Body); } else if (!hasPredicateArg) { argExpr = info.Element; } aggExpr = new AggregateExpression(returnType, aggType, argExpr); // check for easy to optimize case. If the projection that our aggregate is based on is really the 'group' argument from // the query.GroupBy(xxx, (key, group) => yyy) method then whatever expression we return here will automatically // become part of the select expression that has the group-by clause, so just return the simple aggregate expression. if (projection == this.currentGroupElement) { return(aggExpr); } return(new AggregateSubqueryExpression(info.Alias, aggExpr, subquery)); } return(subquery); }
internal static ProjectionExpression Remove(ProjectionExpression projection, IEnumerable <SelectExpression> selectsToRemove, TextWriter logger) { return((ProjectionExpression) new SubqueryRemover(selectsToRemove, logger).Visit(projection)); }
protected virtual Expression BindGroupBy(Expression source, LambdaExpression keySelector, LambdaExpression elementSelector, LambdaExpression resultSelector) { ProjectionExpression projection = this.VisitSequence(source); this.map[keySelector.Parameters[0]] = projection.Projector; Expression keyExpr = this.Visit(keySelector.Body); Expression elemExpr = projection.Projector; if (elementSelector != null) { this.map[elementSelector.Parameters[0]] = projection.Projector; elemExpr = this.Visit(elementSelector.Body); } // Use ProjectColumns to get group-by expressions from key expression ProjectedColumns keyProjection = this.ProjectColumns(keyExpr, projection.Source.Alias, projection.Source.Alias); IEnumerable <Expression> groupExprs = keyProjection.Columns.Select(c => c.Expression); // make duplicate of source query as basis of element subquery by visiting the source again ProjectionExpression subqueryBasis = this.VisitSequence(source); // recompute key columns for group expressions relative to subquery (need these for doing the correlation predicate) this.map[keySelector.Parameters[0]] = subqueryBasis.Projector; Expression subqueryKey = this.Visit(keySelector.Body); // use same projection trick to get group-by expressions based on subquery ProjectedColumns subqueryKeyPC = this.ProjectColumns(subqueryKey, subqueryBasis.Source.Alias, subqueryBasis.Source.Alias); IEnumerable <Expression> subqueryGroupExprs = subqueryKeyPC.Columns.Select(c => c.Expression); Expression subqueryCorrelation = this.BuildPredicateWithNullsEqual(subqueryGroupExprs, groupExprs); // compute element based on duplicated subquery Expression subqueryElemExpr = subqueryBasis.Projector; if (elementSelector != null) { this.map[elementSelector.Parameters[0]] = subqueryBasis.Projector; subqueryElemExpr = this.Visit(elementSelector.Body); } // build subquery that projects the desired element string elementAlias = this.GetNextAlias(); ProjectedColumns elementPC = this.ProjectColumns(subqueryElemExpr, elementAlias, subqueryBasis.Source.Alias); ProjectionExpression elementSubquery = new ProjectionExpression( new SelectExpression(TypeSystem.GetSequenceType(subqueryElemExpr.Type), elementAlias, elementPC.Columns, subqueryBasis.Source, subqueryCorrelation), elementPC.Projector ); string alias = this.GetNextAlias(); // make it possible to tie aggregates back to this group-by GroupByInfo info = new GroupByInfo(alias, elemExpr); this.groupByMap.Add(elementSubquery, info); Expression resultExpr; if (resultSelector != null) { Expression saveGroupElement = this.currentGroupElement; this.currentGroupElement = elementSubquery; // compute result expression based on key & element-subquery this.map[resultSelector.Parameters[0]] = keyProjection.Projector; this.map[resultSelector.Parameters[1]] = elementSubquery; resultExpr = this.Visit(resultSelector.Body); this.currentGroupElement = saveGroupElement; } else { // result must be IGrouping<K,E> resultExpr = Expression.New( typeof(Grouping <,>).MakeGenericType(keyExpr.Type, subqueryElemExpr.Type).GetConstructors()[0], new Expression[] { keyExpr, elementSubquery } ); } ProjectedColumns pc = this.ProjectColumns(resultExpr, alias, projection.Source.Alias); // make it possible to tie aggregates back to this group-by Expression projectedElementSubquery = ((NewExpression)pc.Projector).Arguments[1]; this.groupByMap.Add(projectedElementSubquery, info); return(new ProjectionExpression( new SelectExpression(TypeSystem.GetSequenceType(resultExpr.Type), alias, pc.Columns, projection.Source, null, null, groupExprs), pc.Projector )); }
internal static ProjectionExpression Remove(ProjectionExpression projection, TextWriter logger, params SelectExpression[] selectsToRemove) { return(Remove(projection, (IEnumerable <SelectExpression>)selectsToRemove, logger)); }