protected override IEnumerable <Entity> ExecuteInternal(IDictionary <string, DataSource> dataSources, IQueryExecutionOptions options, IDictionary <string, Type> parameterTypes, IDictionary <string, object> parameterValues) { var source = Source.Execute(dataSources, options, parameterTypes, parameterValues); var schema = GetSchema(dataSources, parameterTypes); var expressions = Sorts.Select(sort => sort.Expression.Compile(schema, parameterTypes)).ToList(); if (PresortedCount == 0) { // We haven't been able to fold any of the sort orders down to the source, so we need to apply // them all again here IOrderedEnumerable <Entity> sortedSource; if (Sorts[0].SortOrder == SortOrder.Descending) { sortedSource = source.OrderByDescending(e => expressions[0](e, parameterValues, options)); } else { sortedSource = source.OrderBy(e => expressions[0](e, parameterValues, options)); } for (var i = 1; i < Sorts.Count; i++) { var expr = expressions[i]; if (Sorts[i].SortOrder == SortOrder.Descending) { sortedSource = sortedSource.ThenByDescending(e => expr(e, parameterValues, options)); } else { sortedSource = sortedSource.ThenBy(e => expr(e, parameterValues, options)); } } foreach (var entity in sortedSource) { yield return(entity); } } else { // We have managed to fold some but not all of the sorts down to the source. Take records // from the source that have equal values of the sorts that have been folded, then sort those // subsets individually on the remaining sorts var preSortedColumns = Sorts .Take(PresortedCount) .Select(s => { schema.ContainsColumn(((ColumnReferenceExpression)s.Expression).GetColumnName(), out var colName); return(colName); }) .ToList(); var subset = new List <Entity>(); var comparer = new DistinctEqualityComparer(preSortedColumns); foreach (var next in source) { // Get the other values to sort on from this next record var nextSortedValues = expressions .Take(PresortedCount) .Select(expr => expr(next, parameterValues, options)) .ToList(); // If we've already got a subset to work on, check if this fits in the same subset if (subset.Count > 0 && !comparer.Equals(subset[0], next)) { // A value is different, so this record doesn't fit in the same subset. Sort the subset // by the remaining sorts and return the values from it SortSubset(subset, schema, parameterTypes, parameterValues, expressions, options); foreach (var entity in subset) { yield return(entity); } // Now clear out the previous subset so we can move on to the next subset.Clear(); } subset.Add(next); } // Sort and return the final subset SortSubset(subset, schema, parameterTypes, parameterValues, expressions, options); foreach (var entity in subset) { yield return(entity); } } }
protected override IEnumerable <Entity> ExecuteInternal(IDictionary <string, DataSource> dataSources, IQueryExecutionOptions options, IDictionary <string, Type> parameterTypes, IDictionary <string, object> parameterValues) { var schema = Source.GetSchema(dataSources, parameterTypes); var groupByCols = GetGroupingColumns(schema); var isScalarAggregate = IsScalarAggregate; InitializeAggregates(schema, parameterTypes); Entity currentGroup = null; var comparer = new DistinctEqualityComparer(groupByCols); var aggregates = CreateAggregateFunctions(parameterValues, options, false); var states = isScalarAggregate ? ResetAggregates(aggregates) : null; foreach (var entity in Source.Execute(dataSources, options, parameterTypes, parameterValues)) { if (!isScalarAggregate || currentGroup != null) { var startNewGroup = currentGroup == null; if (currentGroup != null && !comparer.Equals(currentGroup, entity)) { // We've reached the end of the previous group - return that row now var result = new Entity(); for (var i = 0; i < groupByCols.Count; i++) { result[groupByCols[i]] = currentGroup[groupByCols[i]]; } foreach (var aggregate in GetValues(states)) { result[aggregate.Key] = aggregate.Value; } yield return(result); startNewGroup = true; } if (startNewGroup) { currentGroup = entity; states = ResetAggregates(aggregates); } } foreach (var func in states.Values) { func.AggregateFunction.NextRecord(entity, func.State); } } if (states != null) { // For scalar aggregates, or for non-scalar aggregates where we've found at least one group, we need to // return the values for the final group var result = new Entity(); for (var i = 0; i < groupByCols.Count; i++) { result[groupByCols[i]] = currentGroup[groupByCols[i]]; } foreach (var aggregate in GetValues(states)) { result[aggregate.Key] = aggregate.Value; } yield return(result); } }