protected virtual Expression VisitAggregate(AggregateExpression node) { Expression arg = Visit(node.Argument); if (arg != node.Argument) { return new AggregateExpression(node.Type, node.AggregateName, arg, node.IsDistinct); } return node; }
// protected override methods protected override Expression VisitAggregate(AggregateExpression node) { WriteAggregateName(node.AggregateName); Write("("); if (node.Argument != null) { VisitValue(node.Argument); } Write(")"); return node; }
// private methods private Expression BindAggregate(Expression source, String aggName, MethodInfo method, LambdaExpression argument, Boolean isRoot) { Type returnType = method.ReturnType; Boolean hasPredicateArg = HasPredicateArg(aggName); Boolean isDistinct = false; Boolean argumentWasPredicate = false; // check for distinct var mcs = source as MethodCallExpression; if (mcs != null && !hasPredicateArg && argument == null) { if (mcs.Method.Name == "Distinct" && mcs.Arguments.Count == 1 && (mcs.Method.DeclaringType == typeof (Queryable) || mcs.Method.DeclaringType == typeof (Enumerable))) { source = mcs.Arguments[0]; isDistinct = true; } } 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 = VisitSequence(source); Expression argExpr = null; if (argument != null) { _map[argument.Parameters[0]] = projection.Projector; argExpr = Visit(argument.Body); } else if (!hasPredicateArg) { argExpr = projection.Projector; } SourceAlias alias = GetNextAlias(); Expression aggExpr = new AggregateExpression(returnType, aggName, argExpr, isDistinct); var select = new SelectExpression(alias, new[] {new ColumnDeclaration("", aggExpr)}, projection.Source, null); if (isRoot) { ParameterExpression p = Expression.Parameter(typeof (IEnumerable<>).MakeGenericType(aggExpr.Type), "node"); LambdaExpression gator = Expression.Lambda(Expression.Call(typeof (Enumerable), "Single", new[] {returnType}, p), p); return new ProjectionExpression(select, new ColumnExpression(returnType, alias, ""), gator); } var subquery = new ScalarExpression(returnType, select); // if we can find the corresponding group-info we can build a special AggregateSubquery node that will enable us to // optimize the node expression later using AggregateRewriter GroupByInfo info; if (!argumentWasPredicate && _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) { _map[argument.Parameters[0]] = info.Element; argExpr = Visit(argument.Body); } else if (!hasPredicateArg) { argExpr = info.Element; } aggExpr = new AggregateExpression(returnType, aggName, argExpr, isDistinct); // check for easy to optimize case. If the projection that our node 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 node expression. if (projection == _currentGroupElement) { return aggExpr; } return new AggregateSubqueryExpression(info.Alias, aggExpr, subquery); } return subquery; }