public AggregateSubqueryExpression(IdentifiableAlias groupByAlias, Expression aggregateInGroupSelect, ScalarExpression aggregateAsSubquery) : base(DbExpressionType.AggregateSubquery, aggregateAsSubquery.Type) { this.aggregateInGroupSelect = aggregateInGroupSelect; this.groupByAlias = groupByAlias; this.aggregateAsSubquery = aggregateAsSubquery; }
protected AggregateSubqueryExpression UpdateAggregateSubquery(AggregateSubqueryExpression aggregate, ScalarExpression subquery) { if (subquery != aggregate.AggregateAsSubquery) { return new AggregateSubqueryExpression(aggregate.GroupByAlias, aggregate.AggregateInGroupSelect, subquery); } return aggregate; }
protected virtual Expression VisitScalar(ScalarExpression scalar) { var select = (SelectExpression)this.Visit(scalar.Select); return this.UpdateScalar(scalar, select); }
protected ScalarExpression UpdateScalar(ScalarExpression scalar, SelectExpression select) { if (select != scalar.Select) { return new ScalarExpression(scalar.Type, select); } return scalar; }
protected override Expression VisitScalar(ScalarExpression subquery) { this.Write("("); this.WriteLine(Indentation.Inner); this.Visit(subquery.Select); this.WriteLine(Indentation.Same); this.Write(")"); this.Indent(Indentation.Outer); return subquery; }
protected virtual bool CompareScalar(ScalarExpression a, ScalarExpression b) { return this.Compare(a.Select, b.Select); }
protected virtual Expression BindAggregate(Expression source, string aggName, Type returnType, LambdaExpression argument, bool isRoot) { bool hasPredicateArg = this.language.AggregateArgumentIsPredicate(aggName); 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.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", new[] { TypeHelper.GetElementType(source.Type) }, 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 || useAlternateArg) { argExpr = projection.Projector; } var alias = this.GetNextAlias(); Expression aggExpr = new AggregateExpression(returnType, aggName, argExpr, isDistinct); var colType = this.language.TypeSystem.GetStorageType(returnType); SelectExpression select = new SelectExpression(alias, new FieldDeclaration[] { new FieldDeclaration("", aggExpr, colType) }, 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 ProjectionExpression(select, new FieldExpression( returnType, this.language.TypeSystem.GetStorageType(returnType), alias, ""), gator); } ScalarExpression 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 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 fields 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 AggregateExpression(returnType, aggName, 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 AggregateSubqueryExpression(info.Alias, aggExpr, subquery); } return subquery; }