protected virtual Expression VisitScalar(ScalarExpression node)
 {
     var select = (SelectExpression) Visit(node.Select);
     if (select != node.Select)
     {
         return new ScalarExpression(node.Type, select);
     }
     return node;
 }
Beispiel #2
0
        // 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;
        }