/// <summary> /// Execute group-by without aggregation on the results. /// </summary> /// <param name="query">The collection to group.</param> /// <param name="maxResults">The max number of elements in a result set.</param> /// <param name="transformation">The group-by transformation as <see cref="ApplyGroupbyClause"/>.</param> /// <param name="keyType">The key type to group by.</param> /// <param name="propertiesToGroupByExpressions">Lambda expression that represents access to the properties to group by.</param> /// <returns>The results of the group by transformation as <see cref="IQueryable"/>.</returns> public IQueryable DoGroupBy(IQueryable query, int maxResults, ApplyGroupbyClause transformation, Type keyType, IEnumerable <LambdaExpression> propertiesToGroupByExpressions) { var propToGroupBy = (propertiesToGroupByExpressions != null) ? propertiesToGroupByExpressions.ToArray() : null; var keySelector = this.GetGroupByProjectionLambda(transformation.SelectedStatements.ToArray(), keyType, propToGroupBy); object comparer = keyType.GetProperty("ComparerInstance").GetValue(null); var results = ExpressionHelpers.GroupBy(query, keySelector, this.Context.ElementClrType, keyType, comparer); var keys = this.GetGroupingKeys(results, keyType, this.Context.ElementClrType); // if group by is not supported in this IQueriable provider convert the grouping into memory implementation object convertedResult = null; int numberOfTempResults; if (QueriableProviderAdapter.ConvertionIsRequiredAsExpressionIfNotSupported(keys, maxResults, out convertedResult, out numberOfTempResults)) { keys = convertedResult as IQueryable; } var keysToReturn = ExpressionHelpers.Distinct(keyType, keys); return(keysToReturn); }
/// <summary> /// Execute group-by with aggregation on the results. /// </summary> /// <param name="query">The collection to group.</param> /// <param name="maxResults">The max number of elements in a result set.</param> /// <param name="transformation">The group-by transformation as <see cref="ApplyGroupbyClause"/>.</param> /// <param name="keyType">The key type to group by.</param> /// <param name="propertiesToGroupByExpressions">Lambda expression that represents access to the properties to group by.</param> /// <param name="propertyToAggregateExpression">Lambda expression that represents access to the property to aggregate.</param> /// <param name="keys">Output the collection keys of the grouped results.</param> /// <param name="aggragatedValues">Output the aggregated results.</param> public void DoAggregatedGroupBy( IQueryable query, int maxResults, ApplyGroupbyClause transformation, Type keyType, IEnumerable <LambdaExpression> propertiesToGroupByExpressions, LambdaExpression propertyToAggregateExpression, out IQueryable keys, out object[] aggragatedValues) { var propToGroupBy = (propertiesToGroupByExpressions != null) ? propertiesToGroupByExpressions.ToArray() : null; var keySelector = this.GetGroupByProjectionLambda(transformation.SelectedStatements.ToArray(), keyType, propToGroupBy); object comparer = keyType.GetProperty("ComparerInstance").GetValue(null); var groupingResults = ExpressionHelpers.GroupBy(query, keySelector, this.Context.ElementClrType, keyType, comparer); var aggregationImplementation = AggregationMethodsImplementations.GetAggregationImplementation(transformation.Aggregate.AggregationMethod); // if group by is not supported in this IQueriable provider convert the grouping into memory implementation object convertedResult = null; int numberOfTempResults; if (QueriableProviderAdapter.ConvertionIsRequiredAsExpressionIfNotSupported(groupingResults, maxResults, out convertedResult, out numberOfTempResults)) { groupingResults = convertedResult as IQueryable; } var resType = typeof(List <>).MakeGenericType(this.Context.ElementClrType); keys = this.GetGroupingKeys(groupingResults, keyType, this.Context.ElementClrType); var groupedValues = this.GetGroupingValues(groupingResults, keyType, resType, this.Context.ElementClrType); // In case of paging due to memory execution of unsupported functions keys may not be distinct. // Here we make sure that keys are distinct and all values that belong to a key are written to the right list. List <object> distictKeys; List <object> groupedValuesPerKey; if (numberOfTempResults > 1) { this.CombineValuesListsPerKey(keys.AllElements(), groupedValues.AllElements(), out distictKeys, out groupedValuesPerKey); keys = distictKeys.AsQueryable(); } groupedValuesPerKey = groupedValues.AllElements(); int numberOfResults = groupedValuesPerKey.Count; var tmpAggragatedValues = new object[numberOfResults]; var projectionLambda = AggregationImplementationBase.GetProjectionLambda(this.Context.ElementClrType, transformation.Aggregate, propertyToAggregateExpression); string[] aggregationParams = AggregationImplementationBase.GetAggregationParams(transformation.Aggregate.AggregationMethod); Parallel.For(0, numberOfResults, (i => { IQueryable valuesAsQueryable; if (groupedValuesPerKey[i] is IEnumerable <object> ) { valuesAsQueryable = ExpressionHelpers.Cast(this.Context.ElementClrType, (groupedValuesPerKey[i] as IEnumerable <Object>).AsQueryable()); } else { valuesAsQueryable = ExpressionHelpers.Cast(this.Context.ElementClrType, (new List <Object>() { groupedValuesPerKey[i] }).AsQueryable()); } IQueryable queryToUse = valuesAsQueryable; if (transformation.Aggregate.AggregatableProperty.Contains('/')) { queryToUse = AggregationImplementationBase.FilterNullValues(query, this.Context.ElementClrType, transformation.Aggregate); } var aggragationResult = aggregationImplementation.DoAggregatinon(this.Context.ElementClrType, queryToUse, transformation.Aggregate, projectionLambda, aggregationParams); tmpAggragatedValues[i] = aggragationResult; })); aggragatedValues = tmpAggragatedValues; }