/// <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()); }
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); }
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); }