protected DbAggregateExpression UpdateAggregate(DbAggregateExpression aggregate, Type type, DbAggregateType aggType, Expression arg, bool isDistinct) { if (type != aggregate.Type || aggType != aggregate.AggregateType || arg != aggregate.Argument || isDistinct != aggregate.IsDistinct) return new DbAggregateExpression(type, aggType, arg, isDistinct); return aggregate; }
protected virtual Expression VisitAggregate(DbAggregateExpression aggregate) { Expression arg = Visit(aggregate.Argument); return UpdateAggregate(aggregate, aggregate.Type, aggregate.AggregateType, arg, aggregate.IsDistinct); }
protected override Expression VisitAggregate(DbAggregateExpression aggregate) { this.hasAggregate = true; return aggregate; }
private Expression BindAggregate(Expression source, MethodInfo method, LambdaExpression argument, bool isRoot) { Type returnType = method.ReturnType; DbAggregateType aggType = this.GetAggregateType(method.Name); bool hasPredicateArg = this.HasPredicateArg(aggType); bool isDistinct = false; bool argumentWasPredicate = false; bool useAlternateArg = false; // check for distinct MethodCallExpression 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)) && this.mapping.Language.AllowDistinctInAggregates) { 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; } DbProjectionExpression 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 || useAlternateArg) { argExpr = projection.Projector; } var alias = this.GetNextAlias(); var pc = this.ProjectColumns(projection.Projector, alias, projection.Select.Alias); Expression aggExpr = new DbAggregateExpression(returnType, aggType, argExpr, isDistinct); DbSelectExpression select = new DbSelectExpression(alias, new DbColumnDeclaration[] { new DbColumnDeclaration("", aggExpr) }, projection.Select, null); if (isRoot) { ParameterExpression p = Expression.Parameter(typeof(IEnumerable<>).MakeGenericType(aggExpr.Type), "p"); LambdaExpression gator = Expression.Lambda(Expression.Call(typeof(Enumerable), "Single", new Type[] { returnType }, p), p); return new DbProjectionExpression(select, new DbColumnExpression(returnType, null, alias, ""), gator); } DbScalarExpression subquery = new DbScalarExpression(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 || useAlternateArg) { argExpr = info.Element; } aggExpr = new DbAggregateExpression(returnType, aggType, argExpr, isDistinct); // 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 DbAggregateSubqueryExpression(info.Alias, aggExpr, subquery); } return subquery; }
protected override Expression VisitAggregate(DbAggregateExpression aggregate) { this.Write(GetAggregateName(aggregate.AggregateType)); this.Write("("); if (aggregate.IsDistinct) { this.Write("DISTINCT "); } if (aggregate.Argument != null) { this.VisitValue(aggregate.Argument); } else if (RequiresAsteriskWhenNoArgument(aggregate.AggregateType)) { this.Write("*"); } this.Write(")"); return aggregate; }
protected virtual bool CompareAggregate(DbAggregateExpression a, DbAggregateExpression b) { return (a.AggregateType == b.AggregateType && Compare(a.Argument, b.Argument)); }