예제 #1
0
        protected virtual Expression BindGroupBy(Expression source, LambdaExpression keySelector, LambdaExpression elementSelector, LambdaExpression resultSelector)
        {
            ProjectionExpression projection = this.VisitSequence(source);



            this.map[keySelector.Parameters[0]] = projection.Projector;

            Expression keyExpr = this.Visit(keySelector.Body);

            Expression elemExpr = projection.Projector;

            if (elementSelector != null)
            {
                this.map[elementSelector.Parameters[0]] = projection.Projector;

                elemExpr = this.Visit(elementSelector.Body);
            }



            // Use ProjectColumns to get group-by expressions from key expression

            ProjectedColumns keyProjection = this.ProjectColumns(keyExpr, projection.Source.Alias, projection.Source.Alias);

            IEnumerable <Expression> groupExprs = keyProjection.Columns.Select(c => c.Expression);

            // make duplicate of source query as basis of element subquery by visiting the source again

            ProjectionExpression subqueryBasis = this.VisitSequence(source);

            // recompute key columns for group expressions relative to subquery (need these for doing the correlation predicate)

            this.map[keySelector.Parameters[0]] = subqueryBasis.Projector;

            Expression subqueryKey = this.Visit(keySelector.Body);



            // use same projection trick to get group-by expressions based on subquery

            ProjectedColumns subqueryKeyPC = this.ProjectColumns(subqueryKey, subqueryBasis.Source.Alias, subqueryBasis.Source.Alias);

            IEnumerable <Expression> subqueryGroupExprs = subqueryKeyPC.Columns.Select(c => c.Expression);

            Expression subqueryCorrelation = this.BuildPredicateWithNullsEqual(subqueryGroupExprs, groupExprs);



            // compute element based on duplicated subquery

            Expression subqueryElemExpr = subqueryBasis.Projector;

            if (elementSelector != null)
            {
                this.map[elementSelector.Parameters[0]] = subqueryBasis.Projector;

                subqueryElemExpr = this.Visit(elementSelector.Body);
            }



            // build subquery that projects the desired element

            string elementAlias = this.GetNextAlias();

            ProjectedColumns elementPC = this.ProjectColumns(subqueryElemExpr, elementAlias, subqueryBasis.Source.Alias);

            ProjectionExpression elementSubquery =

                new ProjectionExpression(

                    new SelectExpression(TypeSystem.GetSequenceType(subqueryElemExpr.Type), elementAlias, elementPC.Columns, subqueryBasis.Source, subqueryCorrelation),

                    elementPC.Projector

                    );

            string alias = this.GetNextAlias();

            // make it possible to tie aggregates back to this group-by

            GroupByInfo info = new GroupByInfo(alias, elemExpr);

            this.groupByMap.Add(elementSubquery, info);

            Expression resultExpr;

            if (resultSelector != null)
            {
                Expression saveGroupElement = this.currentGroupElement;

                this.currentGroupElement = elementSubquery;

                // compute result expression based on key & element-subquery

                this.map[resultSelector.Parameters[0]] = keyProjection.Projector;

                this.map[resultSelector.Parameters[1]] = elementSubquery;

                resultExpr = this.Visit(resultSelector.Body);

                this.currentGroupElement = saveGroupElement;
            }

            else
            {
                // result must be IGrouping<K,E>

                resultExpr = Expression.New(

                    typeof(Grouping <,>).MakeGenericType(keyExpr.Type, subqueryElemExpr.Type).GetConstructors()[0],

                    new Expression[] { keyExpr, elementSubquery }

                    );
            }

            ProjectedColumns pc = this.ProjectColumns(resultExpr, alias, projection.Source.Alias);

            // make it possible to tie aggregates back to this group-by

            Expression projectedElementSubquery = ((NewExpression)pc.Projector).Arguments[1];

            this.groupByMap.Add(projectedElementSubquery, info);

            return(new ProjectionExpression(

                       new SelectExpression(TypeSystem.GetSequenceType(resultExpr.Type), alias, pc.Columns, projection.Source, null, null, groupExprs),

                       pc.Projector

                       ));
        }