/// <summary> /// Get the list of parameter expression to be binded to the method call, /// If the number of arguments provided by the query does not match the method signature this method will ignore non required arguments or create arguments with default values. /// </summary> /// <param name="samplingMethod">The sampling method string that contain the arguments</param> /// <param name="method">The <see cref="MethodInfo"/> of the sampling method to call</param> /// <returns>Array of expressions</returns> private static Expression[] GetAggregationArgumentsExpressions(string samplingMethod, MethodInfo method) { Expression[] aggregationParamsExpressions = null; string[] aggregationParamsAsStrings = AggregationImplementationBase.GetAggregationParams(samplingMethod); var expcetedParameters = method.GetParameters(); if (aggregationParamsAsStrings != null && aggregationParamsAsStrings.Any()) { aggregationParamsExpressions = ParseAggregationParams(aggregationParamsAsStrings, expcetedParameters); if (expcetedParameters.Length != aggregationParamsAsStrings.Length + 1) { if (aggregationParamsAsStrings.Length > expcetedParameters.Length - 1) { var tmp = new Expression[expcetedParameters.Length - 1]; Array.Copy(aggregationParamsExpressions, tmp, expcetedParameters.Length - 1); aggregationParamsExpressions = tmp; } else { var tmp = new List <Expression>(); tmp.AddRange(aggregationParamsExpressions); GetDefaultArgumentsExpressions(aggregationParamsExpressions.Length, expcetedParameters.Length - 1, expcetedParameters, tmp); aggregationParamsExpressions = tmp.ToArray(); } } } else { var tmp = new List <Expression>(); GetDefaultArgumentsExpressions(0, expcetedParameters.Length - 1, expcetedParameters, tmp); aggregationParamsExpressions = tmp.ToArray(); } return(aggregationParamsExpressions); }
/// <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; }