/// <summary>
        /// Gets the <see cref="IOrderByBinder"/>.
        /// </summary>
        /// <param name="context">The query context.</param>
        /// <returns>The built <see cref="IOrderByBinder"/>.</returns>
        public static IOrderByBinder GetOrderByBinder(this ODataQueryContext context)
        {
            if (context == null)
            {
                throw Error.ArgumentNull(nameof(context));
            }

            IOrderByBinder binder = context.RequestContainer?.GetService <IOrderByBinder>();

            return(binder ?? new OrderByBinder());
        }
Beispiel #2
0
        private static IQueryable AddOrderByQueryForProperty(IOrderByBinder orderByBinder,
                                                             OrderByClause orderbyClause, IQueryable querySoFar, QueryBinderContext binderContext, bool alreadyOrdered)
        {
            // Remove Thenby (make Thenby == null) to make sure we only apply the top orderby
            // TODO: need to refactor it later.
            orderbyClause = new OrderByClause(null, orderbyClause.Expression, orderbyClause.Direction, orderbyClause.RangeVariable);

            querySoFar = orderByBinder.ApplyBind(querySoFar, orderbyClause, binderContext, alreadyOrdered);

            return(querySoFar);
        }
        /// <summary>
        /// Translates an OData $orderby represented by <see cref="OrderByClause"/> to <see cref="Expression"/> and apply to <see cref="IQueryable" />.
        /// </summary>
        /// <param name="binder">The given filter binder.</param>
        /// <param name="query">The given source.</param>
        /// <param name="orderByClause">The filter clause.</param>
        /// <param name="context">The query binder context.</param>
        /// <param name="alreadyOrdered">The boolean value indicating whether it's ordered or not.</param>
        /// <returns>The applied result.</returns>
        public static IQueryable ApplyBind(this IOrderByBinder binder, IQueryable query, OrderByClause orderByClause, QueryBinderContext context, bool alreadyOrdered)
        {
            if (binder == null)
            {
                throw Error.ArgumentNull(nameof(binder));
            }

            if (query == null)
            {
                throw Error.ArgumentNull(nameof(query));
            }

            if (orderByClause == null)
            {
                throw Error.ArgumentNull(nameof(orderByClause));
            }

            if (context == null)
            {
                throw Error.ArgumentNull(nameof(context));
            }

            OrderByBinderResult orderByResult = binder.BindOrderBy(orderByClause, context);
            IQueryable          querySoFar    = query;

            Type elementType           = context.ElementClrType;
            OrderByBinderResult result = orderByResult;

            do
            {
                LambdaExpression orderByExpression = result.OrderByExpression as LambdaExpression;
                Contract.Assert(orderByExpression != null);

                OrderByDirection direction = result.Direction;

                querySoFar = ExpressionHelpers.OrderBy(querySoFar, orderByExpression, direction, elementType, alreadyOrdered);

                alreadyOrdered = true;

                result = result.ThenBy;
            }while (result != null);

            return(querySoFar);
        }
Beispiel #4
0
        private IOrderedQueryable ApplyToCore(IQueryable query, ODataQuerySettings querySettings)
        {
            if (Context.ElementClrType == null)
            {
                throw Error.NotSupported(SRResources.ApplyToOnUntypedQueryOption, "ApplyTo");
            }

            ICollection <OrderByNode> nodes = OrderByNodes;

            bool       alreadyOrdered = false;
            IQueryable querySoFar     = query;

            HashSet <object> propertiesSoFar     = new HashSet <object>();
            HashSet <string> openPropertiesSoFar = new HashSet <string>();
            bool             orderByItSeen       = false;

            IOrderByBinder     binder        = Context.GetOrderByBinder();
            QueryBinderContext binderContext = new QueryBinderContext(Context.Model, querySettings, Context.ElementClrType);

            if (Compute != null)
            {
                binderContext.AddComputedProperties(Compute.ComputeClause.ComputedItems);
            }

            foreach (OrderByNode node in nodes)
            {
                OrderByPropertyNode     propertyNode     = node as OrderByPropertyNode;
                OrderByOpenPropertyNode openPropertyNode = node as OrderByOpenPropertyNode;
                OrderByCountNode        countNode        = node as OrderByCountNode;

                if (propertyNode != null)
                {
                    // Use autonomy class to achieve value equality for HasSet.
                    var edmPropertyWithPath    = new { propertyNode.Property, propertyNode.PropertyPath };
                    OrderByDirection direction = propertyNode.Direction;

                    // This check prevents queries with duplicate properties (e.g. $orderby=Id,Id,Id,Id...) from causing stack overflows
                    if (propertiesSoFar.Contains(edmPropertyWithPath))
                    {
                        throw new ODataException(Error.Format(SRResources.OrderByDuplicateProperty, edmPropertyWithPath.PropertyPath));
                    }

                    propertiesSoFar.Add(edmPropertyWithPath);

                    if (propertyNode.OrderByClause != null)
                    {
                        querySoFar = AddOrderByQueryForProperty(binder, propertyNode.OrderByClause, querySoFar, binderContext, alreadyOrdered);
                    }
                    else
                    {
                        // could have ensure stable orderby property added
                        querySoFar = ExpressionHelpers.OrderByProperty(querySoFar, Context.Model, edmPropertyWithPath.Property, direction, Context.ElementClrType, alreadyOrdered);
                    }

                    alreadyOrdered = true;
                }
                else if (openPropertyNode != null)
                {
                    // This check prevents queries with duplicate properties (e.g. $orderby=Id,Id,Id,Id...) from causing stack overflows
                    if (openPropertiesSoFar.Contains(openPropertyNode.PropertyName))
                    {
                        throw new ODataException(Error.Format(SRResources.OrderByDuplicateProperty, openPropertyNode.PropertyPath));
                    }

                    openPropertiesSoFar.Add(openPropertyNode.PropertyName);
                    Contract.Assert(openPropertyNode.OrderByClause != null);
                    querySoFar     = AddOrderByQueryForProperty(binder, openPropertyNode.OrderByClause, querySoFar, binderContext, alreadyOrdered);
                    alreadyOrdered = true;
                }
                else if (countNode != null)
                {
                    Contract.Assert(countNode.OrderByClause != null);
                    querySoFar     = AddOrderByQueryForProperty(binder, countNode.OrderByClause, querySoFar, binderContext, alreadyOrdered);
                    alreadyOrdered = true;
                }
                else
                {
                    // This check prevents queries with duplicate nodes (e.g. $orderby=$it,$it,$it,$it...) from causing stack overflows
                    if (orderByItSeen)
                    {
                        throw new ODataException(Error.Format(SRResources.OrderByDuplicateIt));
                    }

                    querySoFar     = ExpressionHelpers.OrderByIt(querySoFar, node.Direction, Context.ElementClrType, alreadyOrdered);
                    alreadyOrdered = true;
                    orderByItSeen  = true;
                }
            }

            return(querySoFar as IOrderedQueryable);
        }