private static void GetGroupByParams(string value, out IQueryable data, out ODataQueryContext context, out ODataQuerySettings settings, out ApplyGroupbyClause groupByClause, out DefaultAssembliesResolver assembliesResolver, out GroupByImplementation groupByImplementation, out Type keyType, out IEnumerable <LambdaExpression> propertiesToGroupByExpressions) { string queryOption = "$apply"; data = TestDataSource.CreateData(); settings = new ODataQuerySettings() { PageSize = 2000, HandleNullPropagation = HandleNullPropagationOption.False }; var _settings = settings; var model = TestModelBuilder.CreateModel(new Type[] { typeof(Category), typeof(Product), typeof(Sales) }); context = new ODataQueryContext(model, typeof(Sales), new ODataPath(new ODataPathSegment[] { new EntitySetPathSegment("Sales") })); var _context = context; IEdmNavigationSource source = model.FindDeclaredEntitySet("Sales"); var parser = new ODataQueryOptionParser(model, model.FindDeclaredType("System.Web.OData.Aggregation.Tests.Common.Sales"), source, new Dictionary <string, string>() { { queryOption, value } }); var applyCaluse = parser.ParseApply(); groupByClause = applyCaluse.Transformations.First().Item2 as ApplyGroupbyClause; assembliesResolver = new DefaultAssembliesResolver(); var _assembliesResolver = assembliesResolver; groupByImplementation = new GroupByImplementation() { Context = context }; keyType = groupByImplementation.GetGroupByKeyType(groupByClause); var entityParam = Expression.Parameter(context.ElementClrType, "$it"); propertiesToGroupByExpressions = groupByClause.SelectedPropertiesExpressions.Select( exp => FilterBinder.Bind(exp, _context.ElementClrType, _context.Model, _assembliesResolver, _settings, entityParam)); }
/// <summary> /// Execute the apply query to the given IQueryable. /// </summary> /// <remarks> /// The <see cref="ODataQuerySettings.HandleNullPropagation"/> property specifies /// how this method should handle null propagation. /// </remarks> /// <param name="query">The original <see cref="IQueryable"/>.</param> /// <param name="querySettings">The <see cref="ODataQuerySettings"/> that contains all the query application related settings.</param> /// <param name="assembliesResolver">IAssembliesResolver provided by the framework.</param> /// <param name="aggregationWindowSize">The max number of results to aggregate in each aggregation batch</param> /// <returns>The new <see cref="IQueryable"/> After the apply query has been applied to.</returns> public IQueryable ApplyTo(IQueryable query, ODataQuerySettings querySettings, IAssembliesResolver assembliesResolver, int aggregationWindowSize) { if (query == null) { throw Error.ArgumentNull("query"); } if (querySettings == null) { throw Error.ArgumentNull("querySettings"); } if (Context.ElementClrType == null) { throw Error.NotSupported(SRResources.ApplyToOnUntypedQueryOption, "ApplyTo"); } ApplyClause applyClause = ApplyClause; Contract.Assert(applyClause != null); // Ensure we have decided how to handle null propagation ODataQuerySettings updatedSettings = querySettings; if (querySettings.HandleNullPropagation == HandleNullPropagationOption.Default) { updatedSettings = new ODataQuerySettings(updatedSettings); updatedSettings.HandleNullPropagation = HandleNullPropagationOption.False; } var maxResults = querySettings.PageSize ?? 2000; if (aggregationWindowSize != 0) { maxResults = (aggregationWindowSize < MAX_AGGREGATION_WINDOW_SIZE) ? aggregationWindowSize : MAX_AGGREGATION_WINDOW_SIZE; } // Call InterceptingProvider.Intercept that will create a new <see cref="InterceptingProvider"/> and set its visitors. // InterceptingProvider will wrap the actual IQueryable to implement unsupported operations in memory. var mi = _intercept_mi.MakeGenericMethod(this.Context.ElementClrType); IQueryable results = mi.Invoke(null, new object[] { query, maxResults, null }) as IQueryable; // Each transformation is the input for the next transformation in the transformations list foreach (var transformation in applyClause.Transformations) { switch (transformation.Item1) { case "aggregate": ApplyAggregateClause aggregateClause = transformation.Item2 as ApplyAggregateClause; if (aggregateClause == null) { throw Error.Argument("aggregation transformation type mismatch", transformation.Item2); } LambdaExpression propertyToAggregateExpression = FilterBinder.Bind(aggregateClause.AggregatablePropertyExpression, Context.ElementClrType, Context.Model, assembliesResolver, updatedSettings); var aggregationImplementation = AggregationMethodsImplementations.GetAggregationImplementation(aggregateClause.AggregationMethod); if (results.Provider is InterceptingProvider) { (results.Provider as InterceptingProvider).Combiner = aggregationImplementation.CombineTemporaryResults; } IQueryable queryToUse = results; if (aggregateClause.AggregatableProperty.Contains('/')) { queryToUse = AggregationImplementationBase.FilterNullValues(query, this.Context.ElementClrType, aggregateClause); } var projectionLambda = AggregationImplementationBase.GetProjectionLambda(this.Context.ElementClrType, aggregateClause, propertyToAggregateExpression); string[] aggregationParams = AggregationImplementationBase.GetAggregationParams(aggregateClause.AggregationMethod); var aggragationResult = aggregationImplementation.DoAggregatinon(this.Context.ElementClrType, queryToUse, aggregateClause, projectionLambda, aggregationParams); var aliasType = aggregationImplementation.GetResultType(this.Context.ElementClrType, aggregateClause); results = this.ProjectResult(aggragationResult, aggregateClause.Alias, aliasType); Context = new ODataQueryContext(this.Context.Model, results.ElementType); break; case "groupby": IEnumerable <LambdaExpression> propertiesToGroupByExpressions = null; var groupByImplementation = new GroupByImplementation() { Context = this.Context }; var groupByClause = transformation.Item2 as ApplyGroupbyClause; if (groupByClause == null) { throw Error.Argument("aggregation transformation type mismatch", transformation.Item2); } var entityParam = Expression.Parameter(this.Context.ElementClrType, "$it"); if (groupByClause.SelectedPropertiesExpressions != null) { propertiesToGroupByExpressions = groupByClause.SelectedPropertiesExpressions.Select( exp => FilterBinder.Bind( exp, this.Context.ElementClrType, this.Context.Model, assembliesResolver, updatedSettings, entityParam)); } var keyType = groupByImplementation.GetGroupByKeyType(groupByClause); if (groupByClause.Aggregate == null) { // simple group-by without aggregation method results = groupByImplementation.DoGroupBy(results, maxResults, groupByClause, keyType, propertiesToGroupByExpressions); } else { IQueryable keys = null; propertyToAggregateExpression = FilterBinder.Bind(groupByClause.Aggregate.AggregatablePropertyExpression, Context.ElementClrType, Context.Model, assembliesResolver, updatedSettings); object[] aggragatedValues = null; groupByImplementation.DoAggregatedGroupBy(results, maxResults, groupByClause, keyType, propertiesToGroupByExpressions, propertyToAggregateExpression, out keys, out aggragatedValues); results = ProjectGroupedResult(groupByClause, keys, aggragatedValues, keyType, Context); } Context = new ODataQueryContext(this.Context.Model, results.ElementType); break; case "filter": var filterClause = transformation.Item2 as ApplyFilterClause; if (filterClause == null) { throw Error.Argument("aggregation transformation type mismatch", transformation.Item2); } var filterImplementation = new FilterImplementation() { Context = this.Context }; results = filterImplementation.DoFilter(results, filterClause, querySettings, this._queryOptionParser); break; default: throw Error.NotSupported("aggregation not supported", transformation.Item1); } } object convertedResult = null; return(results); }