protected override Expression VisitAggregate(AggregateExpression aggregate) { sb.Append(GetAggregateName(aggregate.AggregateType)); sb.Append("("); if (aggregate.Argument != null) { this.Visit(aggregate.Argument); } else if (RequiresAsteriskWhenNoArgument(aggregate.AggregateType)) { sb.Append("*"); } sb.Append(")"); return(aggregate); }
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); }