示例#1
0
        /// <summary>
        /// Generate a new type for the group-by results and return an instance of the results with the aggregated values.
        /// </summary>
        /// <param name="groupByTrasformation">The group-by transformation clause.</param>
        /// <param name="keys">The collection of the group-by keys.</param>
        /// <param name="aggragatedValues">The results of the group-by aggregation.</param>
        /// <param name="keyType">The group-by key type.</param>
        /// <param name="context">The OData query context.</param>
        /// <returns>The group-by results as <see cref="IQueryable"/>.</returns>
        private IQueryable ProjectGroupedResult(ApplyGroupbyClause groupByTrasformation, IQueryable keys, object[] aggragatedValues, Type keyType, ODataQueryContext context)
        {
            List <object> result         = new List <object>();
            var           keyProperties  = keyType.GetProperties();
            var           projectionType = this.GetAggregationResultProjectionType(groupByTrasformation, keyType);

            int i = 0;

            foreach (var key in keys)
            {
                var objToProject = Activator.CreateInstance(projectionType);
                var pi           = projectionType.GetProperty(groupByTrasformation.Aggregate.Alias);
                pi.SetValue(objToProject, aggragatedValues[i]);

                foreach (var key_pi in keyProperties)
                {
                    if (key_pi.Name == "ComparerInstance")
                    {
                        continue;
                    }
                    pi = projectionType.GetProperty(key_pi.Name);
                    pi.SetValue(objToProject, key_pi.GetValue(key));
                }
                result.Add(objToProject);
                i++;
            }

            return(ExpressionHelpers.Cast(projectionType, result.AsQueryable()));
        }
示例#2
0
        /// <summary>
        /// Helper method to create a new dynamic type for the group-by key.
        /// </summary>
        /// <param name="transformation">The group-by query.</param>
        /// <returns>The type of the key which was dynamically generated.</returns>
        internal Type GetGroupByKeyType(ApplyGroupbyClause transformation)
        {
            Contract.Assert(transformation != null);
            Contract.Assert(transformation.SelectedStatements != null);
            Contract.Assert(transformation.SelectedStatements.Any());

            var keyProperties = new List <Tuple <Type, string> >();
            var selectedStatementsDictionary = GetSelectedStatementsDictionary(transformation.SelectedStatements);

            foreach (var statement in selectedStatementsDictionary)
            {
                // simple property
                var statementString = statement.Value.First();
                if ((statement.Value.Count() == 1) && (statementString == statement.Key))
                {
                    string samplingMethod, alias, samplingProperty;
                    GroupByImplementation.GetSamplingMethod(statementString, out samplingMethod, out alias, out samplingProperty);
                    if (samplingMethod != null)
                    {
                        var pi = this.Context.ElementClrType.GetProperty(samplingProperty);
                        if (pi == null)
                        {
                            throw new ArgumentException(string.Format("Entity does not contain {0}", samplingProperty));
                        }
                        var implementation = SamplingMethodsImplementations.GetAggregationImplementation(samplingMethod);
                        var samplingType   = implementation.GetResultType(pi.PropertyType);
                        keyProperties.Add(new Tuple <Type, string>(samplingType, alias));
                    }
                    else
                    {
                        var propName = statementString.TrimMethodCall().Split(' ').First();
                        var pi       = this.Context.ElementClrType.GetProperty(propName);
                        if (pi == null)
                        {
                            throw new ArgumentException(string.Format("Entity does not contain {0}", propName));
                        }
                        keyProperties.Add(new Tuple <Type, string>(pi.PropertyType, pi.Name));
                    }
                }
                else
                {
                    // complex property
                    var propName = statement.Key.TrimMethodCall();
                    var pi       = this.Context.ElementClrType.GetProperty(propName);
                    if (pi == null)
                    {
                        throw new ArgumentException(string.Format("Entity does not contain {0}", propName));
                    }
                    var newPropertyType = this.GenerateComplexType(pi.PropertyType, statement.Value);
                    keyProperties.Add(new Tuple <Type, string>(newPropertyType, propName));
                }
            }

            return(AggregationTypesGenerator.CreateType(keyProperties.Distinct(new TypeStringTupleComapere()).ToList(), Context, true));
        }
示例#3
0
        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));
        }
示例#4
0
        /// <summary>
        /// Get the type to use for returning aggregation results in a group-by query.
        /// </summary>
        /// <param name="groupByTrasformation">The group by transformation.</param>
        /// <param name="keyType">The type of a group by key.</param>
        /// <returns>The new dynamic type.</returns>
        private Type GetAggregationResultProjectionType(ApplyGroupbyClause groupByTrasformation, Type keyType)
        {
            if (groupByTrasformation.Aggregate == null)
            {
                throw new ArgumentException("group by without aggregate");
            }
            var keyProperties             = new List <Tuple <Type, string> >();
            var aggregationImplementation = AggregationImplementations <AggregationImplementationBase> .GetAggregationImplementation(groupByTrasformation.Aggregate.AggregationMethod);

            var aliasType = aggregationImplementation.GetResultType(Context.ElementClrType, groupByTrasformation.Aggregate);

            keyProperties.Add(new Tuple <Type, string>(aliasType, groupByTrasformation.Aggregate.Alias));
            foreach (var prop in keyType.GetProperties())
            {
                if (prop.Name == "ComparerInstance")
                {
                    continue;
                }
                keyProperties.Add(new Tuple <Type, string>(prop.PropertyType, prop.Name));
            }

            return(AggregationTypesGenerator.CreateType(keyProperties.Distinct(new TypeStringTupleComapere()).ToList(), Context, true));
        }
示例#5
0
        /// <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);
        }
示例#6
0
        /// <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;
        }