/// <summary> /// Initializes a new instance of the <see cref="ODataQueryOptions"/> class based on the incoming request and some metadata information from /// the <see cref="ODataQueryContext"/>. /// </summary> /// <param name="context">The <see cref="ODataQueryContext"/> which contains the <see cref="IEdmModel"/> and some type information</param> /// <param name="request">The incoming request message</param> public ODataQueryOptions(ODataQueryContext context, HttpRequestMessage request) { if (context == null) { throw Error.ArgumentNull("context"); } if (request == null) { throw Error.ArgumentNull("request"); } // remember the context Context = context; // Parse the query from request Uri RawValues = new ODataRawQueryOptions(); IEnumerable<KeyValuePair<string, string>> queryParameters = request.GetQueryNameValuePairs(); foreach (KeyValuePair<string, string> kvp in queryParameters) { switch (kvp.Key) { case "$filter": RawValues.Filter = kvp.Value; ThrowIfEmpty(kvp.Value, "$filter"); Filter = new FilterQueryOption(kvp.Value, context); break; case "$orderby": RawValues.OrderBy = kvp.Value; ThrowIfEmpty(kvp.Value, "$orderby"); OrderBy = new OrderByQueryOption(kvp.Value, context); break; case "$top": RawValues.Top = kvp.Value; ThrowIfEmpty(kvp.Value, "$top"); Top = new TopQueryOption(kvp.Value, context); break; case "$skip": RawValues.Skip = kvp.Value; ThrowIfEmpty(kvp.Value, "$skip"); Skip = new SkipQueryOption(kvp.Value, context); break; case "$select": RawValues.Select = kvp.Value; break; case "$inlinecount": RawValues.InlineCount = kvp.Value; break; case "$expand": RawValues.Expand = kvp.Value; break; case "$skiptoken": RawValues.SkipToken = kvp.Value; break; default: // we don't throw if we can't recognize the query break; } } }
public static string BindOrderByQueryOption(OrderByQueryOption orderByQuery) { StringBuilder sb = new StringBuilder(); if (orderByQuery != null) { sb.Append("order by "); foreach (var orderByNode in orderByQuery.OrderByNodes) { var orderByPropertyNode = orderByNode as OrderByPropertyNode; if (orderByPropertyNode != null) { sb.Append(orderByPropertyNode.Property.Name); sb.Append(orderByPropertyNode.Direction == OrderByDirection.Ascending ? " asc," : " desc,"); } else { throw new ODataException("Only ordering by properties is supported"); } } } if (sb[sb.Length - 1] == ',') { sb.Remove(sb.Length - 1, 1); } return sb.ToString(); }
public virtual void Validate(OrderByQueryOption orderByOption, ODataValidationSettings validationSettings) { if (orderByOption == null) { throw Error.ArgumentNull("orderByOption"); } if (validationSettings == null) { throw Error.ArgumentNull("validationSettings"); } if (validationSettings.AllowedOrderByProperties.Count > 0) { ICollection<OrderByPropertyNode> propertyNodes = orderByOption.PropertyNodes; foreach (OrderByPropertyNode property in propertyNodes) { if (!validationSettings.AllowedOrderByProperties.Contains(property.Property.Name)) { throw new ODataException(Error.Format(SRResources.NotAllowedOrderByProperty, property.Property.Name, "AllowedOrderByProperties")); } } } }
public void ApplyInValidOrderbyQueryThrows(string orderbyValue) { var model = new ODataModelBuilder().Add_Customer_EntityType().Add_Customers_EntitySet().GetEdmModel(); var context = new ODataQueryContext(model, typeof(Customer)); var orderby = new OrderByQueryOption(orderbyValue, context); Assert.Throws<ODataException>(() => orderby.ApplyTo(ODataQueryOptionTest.Customers)); }
public void ValidateAllowsOrderByIt() { // Arrange OrderByQueryOption option = new OrderByQueryOption("$it", new ODataQueryContext(EdmCoreModel.Instance, typeof(int))); ODataValidationSettings settings = new ODataValidationSettings(); // Act & Assert Assert.DoesNotThrow(() => _validator.Validate(option, settings)); }
public void ValidateAllowsOrderByIt_IfExplicitlySpecified() { // Arrange OrderByQueryOption option = new OrderByQueryOption("$it", new ODataQueryContext(EdmCoreModel.Instance, typeof(int)), queryTranslator: null); ODataValidationSettings settings = new ODataValidationSettings { AllowedOrderByProperties = { "$it" } }; // Act & Assert Assert.DoesNotThrow(() => _validator.Validate(option, settings)); }
public void ValidateAllowsOrderByIt() { // Arrange OrderByQueryOption option = new OrderByQueryOption("$it", _context); ODataValidationSettings settings = new ODataValidationSettings(); // Act & Assert Assert.DoesNotThrow(() => _validator.Validate(option, settings)); }
public void CanConstructValidFilterQuery(string orderbyValue) { var model = new ODataModelBuilder().Add_Customer_EntityType().Add_Customers_EntitySet().GetEdmModel(); var context = new ODataQueryContext(model, typeof(Customer)); var orderby = new OrderByQueryOption(orderbyValue, context); Assert.Same(context, orderby.Context); Assert.Equal(orderbyValue, orderby.RawValue); }
// Disallow the 'desc' parameter for $orderby option. public override void Validate(OrderByQueryOption orderByOption, ODataValidationSettings validationSettings) { if (orderByOption.OrderByNodes.Any( node => node.Direction == OrderByDirection.Descending)) { throw new ODataException("The 'desc' option is not supported."); } base.Validate(orderByOption, validationSettings); }
public void ValidateWillAllowId() { // Arrange OrderByQueryOption option = new OrderByQueryOption("Id", _context); ODataValidationSettings settings = new ODataValidationSettings(); settings.AllowedOrderByProperties.Add("Id"); // Act & Assert Assert.DoesNotThrow(() => _validator.Validate(option, settings)); }
public void ValidateWillNotAllowName() { // Arrange OrderByQueryOption option = new OrderByQueryOption("Name", _context); ODataValidationSettings settings = new ODataValidationSettings(); settings.AllowedOrderByProperties.Add("Id"); // Act & Assert Assert.Throws<ODataException>(() => _validator.Validate(option, settings), "Order by 'Name' is not allowed. To allow it, set the 'AllowedOrderByProperties' property on QueryableAttribute or QueryValidationSettings."); }
public void ValidateDisallowsOrderByIt_IfTurnedOff() { // Arrange _context = new ODataQueryContext(EdmCoreModel.Instance, typeof(int)); OrderByQueryOption option = new OrderByQueryOption("$it", _context, queryTranslator: null); ODataValidationSettings settings = new ODataValidationSettings(); settings.AllowedOrderByProperties.Add("dummy"); // Act & Assert Assert.Throws<ODataException>( () => _validator.Validate(option, settings), "Order by '$it' is not allowed. To allow it, set the 'AllowedOrderByProperties' property on EnableQueryAttribute or QueryValidationSettings."); }
public static HttpRequestMessage BuildCustomRequest(this ODataQueryOptions originalOptions, out TopQueryOption top, out SkipQueryOption skip, out OrderByQueryOption orderBy) { top = null; skip = null; orderBy = null; // cuttin out $top and $skip from the request HttpRequestMessage customRequest = originalOptions.Request; if (customRequest.Properties.ContainsKey(HttpPropertyKeys.RequestQueryNameValuePairsKey)) { Uri uri = originalOptions.Request.RequestUri; var pairs = customRequest.Properties[HttpPropertyKeys.RequestQueryNameValuePairsKey] as IEnumerable<KeyValuePair<string, string>>; if (pairs != null) { IEnumerable<KeyValuePair<string, string>> jQueryNameValuePairs = new FormDataCollection(uri).GetJQueryNameValuePairs(); var updatedPairs = new List<KeyValuePair<string, string>>(); foreach (var pair in jQueryNameValuePairs) { if (pair.Key.Equals("$top")) { top = originalOptions.Top; } else if (pair.Key.Equals("$skip")) { skip = originalOptions.Skip; } else if (pair.Key.Equals("$orderby")) { orderBy = originalOptions.OrderBy; } else { updatedPairs.Add(pair); } } customRequest.Properties.Remove(HttpPropertyKeys.RequestQueryNameValuePairsKey); customRequest.Properties.Add(HttpPropertyKeys.RequestQueryNameValuePairsKey, updatedPairs); } } return customRequest; }
public void ValidateWillNotAllowMultipleProperties() { // Arrange OrderByQueryOption option = new OrderByQueryOption("Name desc, Id asc", _context); ODataValidationSettings settings = new ODataValidationSettings(); Assert.DoesNotThrow(() => _validator.Validate(option, settings)); settings.AllowedOrderByProperties.Add("Address"); settings.AllowedOrderByProperties.Add("Name"); // Act & Assert Assert.Throws<ODataException>(() => _validator.Validate(option, settings), "Order by 'Id' is not allowed. To allow it, set the 'AllowedOrderByProperties' property on QueryableAttribute or QueryValidationSettings."); }
public void GetQueryNodeParsesQuery() { var model = new ODataModelBuilder().Add_Customer_EntityType().Add_Customers_EntitySet().GetEdmModel(); var context = new ODataQueryContext(model, typeof(Customer), "Customers"); var orderby = new OrderByQueryOption("Name,Website", context); var node = orderby.QueryNode; Assert.Equal(QueryNodeKind.PropertyAccess, node.Expression.Kind); var websiteNode = node.Expression as PropertyAccessQueryNode; Assert.Equal("Website", websiteNode.Property.Name); var nameNode = ((OrderByQueryNode)node.Collection).Expression; Assert.Equal(QueryNodeKind.PropertyAccess, nameNode.Kind); Assert.Equal("Name", ((PropertyAccessQueryNode)nameNode).Property.Name); }
public void ApplyTo_NestedProperties_DoesNotHandleNullPropagation_IfExplicitInSettings() { // Arrange var model = new ODataModelBuilder().Add_Customer_EntityType_With_Address().Add_Customers_EntitySet().GetServiceModel(); var orderByOption = new OrderByQueryOption("Address/City asc", new ODataQueryContext(model, typeof(Customer)), null); var customers = (new List<Customer>{ new Customer { CustomerId = 1, Address = null }, new Customer { CustomerId = 2, Address = new Address { City = "B" } }, new Customer { CustomerId = 3, Address = new Address { City = "A" } } }).AsQueryable(); ODataQuerySettings settings = new ODataQuerySettings { HandleNullPropagation = HandleNullPropagationOption.False }; // Act & Assert Assert.Throws<NullReferenceException>(() => orderByOption.ApplyTo(customers, settings).ToArray()); }
public void PropertyNodes_Getter_Parses_Query() { // Arrange var model = new ODataModelBuilder().Add_Customer_EntityType().Add_Customers_EntitySet().GetEdmModel(); var context = new ODataQueryContext(model, typeof(Customer), "Customers"); var orderby = new OrderByQueryOption("Name,Website", context); // Act ICollection<OrderByPropertyNode> nodes = orderby.PropertyNodes; // Assert Assert.NotNull(nodes); Assert.Equal(2, nodes.Count); Assert.Equal("Name", nodes.First().Property.Name); Assert.Equal("Website", nodes.Last().Property.Name); }
public void PropertyNodes_Getter_Parses_Query() { // Arrange var model = new ODataModelBuilder().Add_Customer_EntityType().Add_Customers_EntitySet().GetEdmModel(); var context = new ODataQueryContext(model, typeof(Customer)); var orderby = new OrderByQueryOption("Name,Website", context); ICollection<OrderByNode> nodes = orderby.OrderByNodes; // Assert Assert.False(nodes.OfType<OrderByItNode>().Any()); IEnumerable<OrderByPropertyNode> propertyNodes = nodes.OfType<OrderByPropertyNode>(); Assert.NotNull(propertyNodes); Assert.Equal(2, propertyNodes.Count()); Assert.Equal("Name", propertyNodes.First().Property.Name); Assert.Equal("Website", propertyNodes.Last().Property.Name); }
/// <summary> /// Validates an <see cref="OrderByQueryOption" />. /// </summary> /// <param name="orderByOption">The $orderby query.</param> /// <param name="validationSettings">The validation settings.</param> public virtual void Validate(OrderByQueryOption orderByOption, ODataValidationSettings validationSettings) { if (orderByOption == null) { throw Error.ArgumentNull("orderByOption"); } if (validationSettings == null) { throw Error.ArgumentNull("validationSettings"); } int nodeCount = 0; for (OrderByClause clause = orderByOption.OrderByClause; clause != null; clause = clause.ThenBy) { nodeCount++; if (nodeCount > validationSettings.MaxOrderByNodeCount) { throw new ODataException(Error.Format(SRResources.OrderByNodeCountExceeded, validationSettings.MaxOrderByNodeCount)); } } if (validationSettings.AllowedOrderByProperties.Count > 0) { IEnumerable<OrderByNode> orderByNodes = orderByOption.OrderByNodes; foreach (OrderByNode node in orderByNodes) { string propertyName = null; OrderByPropertyNode property = node as OrderByPropertyNode; if (property != null) { propertyName = property.Property.Name; } else if ((node as OrderByItNode) != null && !validationSettings.AllowedOrderByProperties.Contains("$it")) { propertyName = "$it"; } if (propertyName != null && !validationSettings.AllowedOrderByProperties.Contains(propertyName)) { throw new ODataException(Error.Format(SRResources.NotAllowedOrderByProperty, propertyName, "AllowedOrderByProperties")); } } } }
public void Validate_ThrowsIfTheLeafOfThePathIsntWithinTheAllowedProperties() { // Arrange IEdmModel model = GetEdmModel(); IEdmEntityType edmType = model.SchemaElements.OfType <IEdmEntityType>().Single(t => t.Name == "LimitedEntity"); ODataQueryContext context = new ODataQueryContext(model, edmType); OrderByQueryOption option = new OrderByQueryOption("ComplexProperty/Value", context); ODataValidationSettings settings = new ODataValidationSettings(); settings.AllowedOrderByProperties.Add("NotSortableProperty"); // Act & Assert OrderByQueryValidator validator = OrderByQueryValidator.GetOrderByQueryValidator(context); ExceptionAssert.Throws <ODataException>(() => validator.Validate(option, settings), "Order by 'Value' is not allowed. To allow it, set the 'AllowedOrderByProperties' property on EnableQueryAttribute or QueryValidationSettings."); }
public static Expression <Func <TElement, bool> > ToExpression <TElement>(this OrderByQueryOption orderBy) { var param = Expression.Parameter(typeof(TElement)); IQueryable queryable = Enumerable.Empty <TElement>().AsQueryable(); if (orderBy != null) { queryable = orderBy.ApplyTo(queryable, new ODataQuerySettings()); var mce = queryable.Expression as MethodCallExpression; if (mce != null) { var quote = mce.Arguments[1] as UnaryExpression; if (quote != null) { return(quote.Operand as Expression <Func <TElement, bool> >); } } } return(Expression.Lambda <Func <TElement, bool> >(Expression.Constant(true), param)); }
public void CanApplySkipOrderby() { var model = new ODataModelBuilder().Add_Customer_EntityType().Add_Customers_EntitySet().GetServiceModel(); var context = new ODataQueryContext(model, typeof(Customer)); var orderbyOption = new OrderByQueryOption("Name", context, queryTranslator: null); var skipOption = new SkipQueryOption("1", context); var customers = (new List<Customer>{ new Customer { CustomerId = 1, Name = "Andy" }, new Customer { CustomerId = 2, Name = "Aaron" }, new Customer { CustomerId = 3, Name = "Alex" } }).AsQueryable(); IQueryable queryable = orderbyOption.ApplyTo(customers); queryable = skipOption.ApplyTo(queryable, new ODataQuerySettings()); var results = ((IQueryable<Customer>)queryable).ToArray(); Assert.Equal(2, results.Length); Assert.Equal(3, results[0].CustomerId); Assert.Equal(1, results[1].CustomerId); }
public ODataQueryOptions(IEnumerable <KeyValuePair <string, string> > queryParameters, ODataQueryContext context, bool aggregate) { if (queryParameters == null) { throw new ArgumentNullException(nameof(queryParameters)); } foreach (var queryParameter in queryParameters) { switch (queryParameter.Key) { case "$filter": if (!aggregate) { Filter = new FilterQueryOption(queryParameter.Value, context); } break; case "$orderby": OrderBy = new OrderByQueryOption(queryParameter.Value, context); break; case "$select": if (aggregate) { Select = new SelectExpandQueryOption(queryParameter.Value, string.Empty, context); } break; case "$top": Top = new TopQueryOption(queryParameter.Value, context); break; case "$format": break; default: throw new ArgumentException($"'{queryParameter.Key}' option is not supported"); } } }
public void PropertyNodes_Getter_Parses_Query() { // Arrange var model = new ODataModelBuilder().Add_Customer_EntityType().Add_Customers_EntitySet().GetEdmModel(); var context = new ODataQueryContext(model, typeof(Customer)) { RequestContainer = new MockContainer() }; var orderby = new OrderByQueryOption("Name,Website", context); ICollection <OrderByNode> nodes = orderby.OrderByNodes; // Assert Assert.False(nodes.OfType <OrderByItNode>().Any()); IEnumerable <OrderByPropertyNode> propertyNodes = nodes.OfType <OrderByPropertyNode>(); Assert.NotNull(propertyNodes); Assert.Equal(2, propertyNodes.Count()); Assert.Equal("Name", propertyNodes.First().Property.Name); Assert.Equal("Website", propertyNodes.Last().Property.Name); }
public void ApplyToEnums_ReturnsCorrectQueryable() { // Arrange var builder = new ODataConventionModelBuilder(); builder.EntitySet<EnumModel>("EnumModels"); var model = builder.GetEdmModel(); var context = new ODataQueryContext(model, typeof(EnumModel)); var orderbyOption = new OrderByQueryOption("Flag", context, null); IEnumerable<EnumModel> enumModels = FilterQueryOptionTest.EnumModelTestData; // Act IQueryable queryable = orderbyOption.ApplyTo(enumModels.AsQueryable()); // Assert Assert.NotNull(queryable); IEnumerable<EnumModel> actualCustomers = Assert.IsAssignableFrom<IEnumerable<EnumModel>>(queryable); Assert.Equal( new int[] { 2, 1, 3 }, actualCustomers.Select(enumModel => enumModel.Id)); }
public void CanApplyOrderBy_WithNestedParameterAlias() { // Arrange var model = new ODataModelBuilder().Add_Customer_EntityType().Add_Customers_EntitySet().GetServiceModel(); var parser = new ODataQueryOptionParser( model, model.FindType("Microsoft.AspNet.OData.Test.Builder.TestModels.Customer"), model.FindDeclaredNavigationSource("Default.Container.Customers"), new Dictionary <string, string> { { "$orderby", "@p1" }, { "@p2", "Name" }, { "@p1", "@p2" } }); var context = new ODataQueryContext(model, typeof(Customer)) { RequestContainer = new MockContainer() }; var orderByOption = new OrderByQueryOption("@p1", context, parser); var customers = (new List <Customer> { new Customer { CustomerId = 1, Name = "Andy" }, new Customer { CustomerId = 2, Name = "Aaron" }, new Customer { CustomerId = 3, Name = "Alex" } }).AsQueryable(); // Act var results = orderByOption.ApplyTo(customers).ToArray(); // Assert Assert.Equal(2, results[0].CustomerId); Assert.Equal(3, results[1].CustomerId); Assert.Equal(1, results[2].CustomerId); }
public void CanApplySkipTopOrderby() { var model = new ODataModelBuilder().Add_Customer_EntityType().Add_Customers_EntitySet().GetServiceModel(); var context = new ODataQueryContext(model, typeof(Customer)) { RequestContainer = new MockContainer() }; var orderbyOption = new OrderByQueryOption("Name", context); var skipOption = new SkipQueryOption("2", context); var topOption = new TopQueryOption("2", context); var customers = (new List <Customer> { new Customer { CustomerId = 1, Name = "Andy" }, new Customer { CustomerId = 2, Name = "Aaron" }, new Customer { CustomerId = 3, Name = "Alex" }, new Customer { CustomerId = 4, Name = "Ace" }, new Customer { CustomerId = 5, Name = "Abner" } }).AsQueryable(); IQueryable queryable = orderbyOption.ApplyTo(customers); queryable = skipOption.ApplyTo(queryable, new ODataQuerySettings()); queryable = topOption.ApplyTo(queryable, new ODataQuerySettings()); var results = ((IQueryable <Customer>)queryable).ToArray(); Assert.Equal(2, results.Length); Assert.Equal(4, results[0].CustomerId); Assert.Equal(3, results[1].CustomerId); }
public void CanOrderByMultipleProperties() { // Arrange var query = new List <Product> { new Product { Id = 2, Price = 1.23m }, new Product { Id = 4, Price = 5.23m }, new Product { Id = 1, Price = 1.23m }, new Product { Id = 3, Price = 0.23m } } .AsQueryable(); var orderByNodes = new List <OrderByNode> { new OrderByNode(nameof(Product.Price), OrderByDirection.Descending), new OrderByNode(nameof(Product.Id)) }; var options = new OrderByQueryOption(orderByNodes); // Act var results = options.ApplyTo(query).ToList(); // Assert Assert.Collection( results, o => Assert.Equal(4, o.Id), o => Assert.Equal(1, o.Id), o => Assert.Equal(2, o.Id), o => Assert.Equal(3, o.Id)); }
private string BuildOrderClause(EdmEntityType edmEntityType, OrderByQueryOption orderByQueryOption) { var orderClause = string.Empty; if (orderByQueryOption != null) { var columns = orderByQueryOption.RawValue.Split(',').AsEnumerable(); var escapedColumns = columns.Select( s => { var columnToOrder = s.Trim().Split(' '); string outputOrder = $"[{columnToOrder[0].Replace('/', _objectHierarchySeparator)}]"; outputOrder += columnToOrder.Count() > 1 ? $" {columnToOrder[1]}" : string.Empty; return(outputOrder); }); orderClause = string.Join(",", escapedColumns); } else { var hasDeclareKey = HasDeclareKey(edmEntityType); if (hasDeclareKey) { var keys = edmEntityType.DeclaredKey; orderClause = string.Join(",", keys.Select(k => $"[{k.Name}]")); } else { var firstProperty = edmEntityType.DeclaredProperties.FirstOrDefault(w => w.Type.IsComplex() == false); if (firstProperty != null) { orderClause = $"[{firstProperty.Name}]"; } } } return(orderClause); }
public void CanOrderByDescending() { // Arrange var query = new List <Product> { new Product { Id = 2 }, new Product { Id = 1 }, new Product { Id = 4 }, new Product { Id = 3 } } .AsQueryable(); var orderByNodes = new List <OrderByNode> { new OrderByNode(nameof(Product.Id), OrderByDirection.Descending) }; var options = new OrderByQueryOption(orderByNodes); // Act var results = options.ApplyTo(query).ToList(); // Assert Assert.Collection( results, o => Assert.Equal(4, o.Id), o => Assert.Equal(3, o.Id), o => Assert.Equal(2, o.Id), o => Assert.Equal(1, o.Id)); }
public SortExpression <T>[] ConvertToSortExpression <T>(OrderByQueryOption orderBy) where T : class { if (orderBy == null) { return(null); } var param = Expression.Parameter(typeof(T)); var sortExpressions = new List <SortExpression <T> >(); var parameters = orderBy.RawValue.Split(','); Expression field = null; var nodeCtr = 0; foreach (var p in parameters) { var raw = p.Replace(" asc", "").Replace(" desc", ""); var props = raw.Split('/'); if (props.Length < 2) { field = Expression.Property(param, raw); } else { field = param; foreach (var sub in props) { field = Expression.Property(field, sub); } } var converted = Expression.Lambda <Func <T, object> >(Expression.Convert(field, typeof(object)), param); sortExpressions.Add(new SortExpression <T>(converted, orderBy.OrderByNodes[nodeCtr].Direction == OrderByDirection.Ascending)); nodeCtr++; } return(sortExpressions.ToArray()); }
public void OrderByIsCaseInsensitive() { // Arrange var query = new List <Product> { new Product { Id = 2 }, new Product { Id = 1 }, new Product { Id = 4 }, new Product { Id = 3 } } .AsQueryable(); var orderByNodes = new List <OrderByNode> { new OrderByNode("iD") }; var options = new OrderByQueryOption(orderByNodes); // Act var results = options.ApplyTo(query).ToList(); // Assert Assert.Collection( results, o => Assert.Equal(1, o.Id), o => Assert.Equal(2, o.Id), o => Assert.Equal(3, o.Id), o => Assert.Equal(4, o.Id)); }
private static List <Order> ToFetchOrder(OrderByQueryOption orderByQueryOption) { var orders = new List <Order>(); if (orderByQueryOption != null) { foreach (var orderByNode in orderByQueryOption.OrderByNodes) { var orderByPropertyNode = orderByNode as OrderByPropertyNode; if (orderByPropertyNode != null) { orders.Add(new Order(orderByPropertyNode.Property.Name, orderByPropertyNode.Direction == OrderByDirection.Descending ? OrderType.Descending : OrderType.Ascending)); } else { throw new ODataException("Only ordering by properties is supported."); } } } return(orders); }
public void ApplyTo_NestedProperties_HandlesNullPropagation_Succeeds() { // Arrange var model = new ODataModelBuilder().Add_Customer_EntityType_With_Address().Add_Customers_EntitySet().GetEdmModel(); var context = new ODataQueryContext(model, typeof(Customer)) { RequestContainer = new MockServiceProvider() }; var orderByOption = new OrderByQueryOption("Address/City asc", context); var customers = (new List <Customer> { new Customer { Id = 1, Address = null }, new Customer { Id = 2, Address = new Address { City = "B" } }, new Customer { Id = 3, Address = new Address { City = "A" } } }).AsQueryable(); // Act ODataQuerySettings settings = new ODataQuerySettings { HandleNullPropagation = HandleNullPropagationOption.True }; var results = orderByOption.ApplyTo(customers, settings).ToArray(); // Assert Assert.Equal(1, results[0].Id); Assert.Equal(3, results[1].Id); Assert.Equal(2, results[2].Id); }
/// <summary> /// Validates an <see cref="OrderByQueryOption" />. /// </summary> /// <param name="orderByOption">The $orderby query.</param> /// <param name="validationSettings">The validation settings.</param> public virtual void Validate(OrderByQueryOption orderByOption, ODataValidationSettings validationSettings) { if (orderByOption == null) { throw Error.ArgumentNull("orderByOption"); } if (validationSettings == null) { throw Error.ArgumentNull("validationSettings"); } if (validationSettings.AllowedOrderByProperties.Count > 0) { IEnumerable <OrderByNode> orderByNodes = orderByOption.OrderByNodes; foreach (OrderByNode node in orderByNodes) { string propertyName = null; OrderByPropertyNode property = node as OrderByPropertyNode; if (property != null) { propertyName = property.Property.Name; } else if ((node as OrderByItNode) != null && !validationSettings.AllowedOrderByProperties.Contains("$it")) { propertyName = "$it"; } if (propertyName != null && !validationSettings.AllowedOrderByProperties.Contains(propertyName)) { throw new ODataException(Error.Format(SRResources.NotAllowedOrderByProperty, propertyName, "AllowedOrderByProperties")); } } } }
public void ApplyTo_NestedProperties_WithDuplicateName_Succeeds() { // Arrange var model = new ODataModelBuilder().Add_Customer_EntityType_With_Address().Add_Customers_EntitySet().GetServiceModel(); var context = new ODataQueryContext(model, typeof(Customer)) { RequestContainer = new MockContainer() }; var orderByOption = new OrderByQueryOption("Address/City,City", context); var customers = (new List <Customer> { new Customer { CustomerId = 1, City = "A", Address = new Address { City = "A" } }, new Customer { CustomerId = 2, City = "B", Address = new Address { City = "B" } }, new Customer { CustomerId = 3, City = "A", Address = new Address { City = "B" } } }).AsQueryable(); // Act var results = orderByOption.ApplyTo(customers).ToArray(); // Assert Assert.Equal(1, results[0].CustomerId); Assert.Equal(3, results[1].CustomerId); Assert.Equal(2, results[2].CustomerId); }
public void Validate_NoException_ForParameterAlias() { // Arrange IEdmModel model = GetEdmModel(); IEdmEntityType edmType = model.SchemaElements.OfType <IEdmEntityType>().Single(t => t.Name == "LimitedEntity"); IEdmEntitySet entitySet = model.FindDeclaredEntitySet("Microsoft.AspNet.OData.Query.Validators.LimitedEntities"); ODataQueryContext context = new ODataQueryContext(model, edmType); OrderByQueryOption option = new OrderByQueryOption( "@p,@q desc", context, new ODataQueryOptionParser( model, edmType, entitySet, new Dictionary <string, string> { { "$orderby", "@p,@q desc" }, { "@p", "Id" }, { "@q", "RelatedEntity/Id" } })); ODataValidationSettings settings = new ODataValidationSettings(); // Act & Assert ExceptionAssert.DoesNotThrow(() => _validator.Validate(option, settings)); }
public void Property_OrderByNodes_WorksWithUnTypedContext() { // Arrange CustomersModelWithInheritance model = new CustomersModelWithInheritance(); ODataQueryContext context = new ODataQueryContext(model.Model, model.Customer); OrderByQueryOption orderBy = new OrderByQueryOption("ID desc", context); // Act & Assert Assert.NotNull(orderBy.OrderByNodes); }
public void ApplyTo_NestedProperties_HandlesNullPropagation_Succeeds() { // Arrange var model = new ODataModelBuilder().Add_Customer_EntityType_With_Address().Add_Customers_EntitySet().GetServiceModel(); var orderByOption = new OrderByQueryOption("Address/City asc", new ODataQueryContext(model, typeof(Customer))); var customers = (new List<Customer>{ new Customer { CustomerId = 1, Address = null }, new Customer { CustomerId = 2, Address = new Address { City = "B" } }, new Customer { CustomerId = 3, Address = new Address { City = "A" } } }).AsQueryable(); // Act var results = orderByOption.ApplyTo(customers).ToArray(); // Assert Assert.Equal(1, results[0].CustomerId); Assert.Equal(3, results[1].CustomerId); Assert.Equal(2, results[2].CustomerId); }
public void OrderByDuplicateItThrows() { // Arrange var context = new ODataQueryContext(EdmCoreModel.Instance, typeof(int)); var orderbyOption = new OrderByQueryOption("$it, $it", context); // Act Assert.Throws<ODataException>( () => orderbyOption.ApplyTo(Enumerable.Empty<int>().AsQueryable()), "Multiple '$it' nodes are not supported in '$orderby'."); }
public void OrderByDuplicatePropertyThrows() { // Arrange var model = new ODataModelBuilder().Add_Customer_EntityType().Add_Customers_EntitySet().GetServiceModel(); var context = new ODataQueryContext(model, typeof(Customer)); var orderbyOption = new OrderByQueryOption("Name, Name", context); // Act Assert.Throws<ODataException>( () => orderbyOption.ApplyTo(Enumerable.Empty<Customer>().AsQueryable()), "Duplicate property named 'Name' is not supported in '$orderby'."); }
public void CanTurnOffValidationForOrderBy() { // Arrange ODataQueryContext context = ValidationTestHelper.CreateCustomerContext(); OrderByQueryOption option = new OrderByQueryOption("Name", context); ODataValidationSettings settings = new ODataValidationSettings(); settings.AllowedOrderByProperties.Add("Id"); // Act & Assert Assert.Throws<ODataException>(() => option.Validate(settings), "Order by 'Name' is not allowed. To allow it, set the 'AllowedOrderByProperties' property on EnableQueryAttribute or QueryValidationSettings."); option.Validator = null; Assert.DoesNotThrow(() => option.Validate(settings)); }
public void OrderBy_Throws_For_Expressions(string orderByQuery) { var model = new ODataModelBuilder().Add_Customer_EntityType_With_Address().Add_Customers_EntitySet().GetServiceModel(); var orderByOption = new OrderByQueryOption(orderByQuery, new ODataQueryContext(model, typeof(Customer))); Assert.Throws<ODataException>( () => orderByOption.OrderByNodes.Count(), "Only ordering by properties is supported for non-primitive collections. Expressions are not supported."); }
public static Func <IQueryable <Creative>, IOrderedQueryable <Creative> > ToExpression <TElement>(this OrderByQueryOption orderBy) { if (orderBy == null) { return(null); } IQueryable queryable = Enumerable.Empty <TElement>().AsQueryable(); var param = Expression.Parameter(typeof(Creative)); //queryable = orderBy.ApplyTo(queryable, new ODataQuerySettings()); IOrderedQueryable <Creative> orderQueryable = (IOrderedQueryable <Creative>)orderBy.ApplyTo(queryable, new ODataQuerySettings()); Func <IQueryable <Creative>, IOrderedQueryable <Creative> > orderingFunc = query => orderQueryable; return(orderingFunc); }
public static HttpRequestMessage BuildCustomRequest(this ODataQueryOptions originalOptions, out TopQueryOption top, out SkipQueryOption skip, out OrderByQueryOption orderBy) { top = null; skip = null; orderBy = null; // cuttin out $top and $skip from the request HttpRequestMessage customRequest = originalOptions.Request; if (customRequest.Properties.ContainsKey(HttpPropertyKeys.RequestQueryNameValuePairsKey)) { Uri uri = originalOptions.Request.RequestUri; var pairs = customRequest.Properties[HttpPropertyKeys.RequestQueryNameValuePairsKey] as IEnumerable <KeyValuePair <string, string> >; if (pairs != null) { IEnumerable <KeyValuePair <string, string> > jQueryNameValuePairs = new FormDataCollection(uri).GetJQueryNameValuePairs(); var updatedPairs = new List <KeyValuePair <string, string> >(); foreach (var pair in jQueryNameValuePairs) { if (pair.Key.Equals("$top")) { top = originalOptions.Top; } else if (pair.Key.Equals("$skip")) { skip = originalOptions.Skip; } else if (pair.Key.Equals("$orderby")) { orderBy = originalOptions.OrderBy; } else { updatedPairs.Add(pair); } } customRequest.Properties.Remove(HttpPropertyKeys.RequestQueryNameValuePairsKey); customRequest.Properties.Add(HttpPropertyKeys.RequestQueryNameValuePairsKey, updatedPairs); } } return(customRequest); }
public override IQueryable ApplyTo(IQueryable query, ODataQuerySettings querySettings) { if (query == null) { throw new ArgumentNullException(nameof(query)); } if (querySettings == null) { throw new ArgumentNullException(nameof(querySettings)); } var result = query; // Construct the actual query and apply them in the following order: filter, orderby, skip, top if (Filter != null) { result = Filter.ApplyTo(result, querySettings, assembliesResolver); } if (InlineCount != null && Request.ODataProperties().TotalCount == null) { long?count = InlineCount.GetEntityCount(result); if (count.HasValue) { Request.ODataProperties().TotalCount = count.Value; } } OrderByQueryOption orderBy = OrderBy; // $skip or $top require a stable sort for predictable results. // Result limits require a stable sort to be able to generate a next page link. // If either is present in the query and we have permission, // generate an $orderby that will produce a stable sort. if (querySettings.EnsureStableOrdering && (Skip != null || Top != null || querySettings.PageSize.HasValue)) { // If there is no OrderBy present, we manufacture a default. // If an OrderBy is already present, we add any missing // properties necessary to make a stable sort. // Instead of failing early here if we cannot generate the OrderBy, // let the IQueryable backend fail (if it has to). orderBy = orderBy == null ? GenerateDefaultOrderBy(Context) : EnsureStableSortOrderBy(orderBy, Context); } if (orderBy != null) { result = orderBy.ApplyTo(result, querySettings); } if (Skip != null) { result = Skip.ApplyTo(result, querySettings); } if (Top != null) { result = Top.ApplyTo(result, querySettings); } if (querySettings.PageSize.HasValue) { bool resultsLimited; result = LimitResults(result, querySettings.PageSize.Value, out resultsLimited); if (resultsLimited && Request.RequestUri != null && Request.RequestUri.IsAbsoluteUri && Request.ODataProperties().NextLink == null) { Uri nextPageLink = GetNextPageLink(Request, querySettings.PageSize.Value); Request.ODataProperties().NextLink = nextPageLink; } } return(result); }
private static string ToOrderByQuery(OrderByQueryOption orderByQuery) { return(NHibernateOrderByBinder.BindOrderByQueryOption(orderByQuery)); }
public async Task <IActionResult> OData(ODataQueryOptions <DeveloperDto> oDataQuery) { var edmModel = EdmModelConfig.GetEdmModel(); var edmEntitySet = edmModel.FindDeclaredEntitySet(nameof(Developer)); var context = new ODataQueryContext(edmModel, typeof(Developer), oDataQuery.Context.Path); var edmType = context.ElementType; var parameters = new Dictionary <string, string>(); if (!string.IsNullOrWhiteSpace(oDataQuery.RawValues.Filter)) { parameters.Add("$filter", oDataQuery.RawValues.Filter); } if (!string.IsNullOrWhiteSpace(oDataQuery.RawValues.Expand)) { parameters.Add("$expand", oDataQuery.RawValues.Expand); } if (!string.IsNullOrWhiteSpace(oDataQuery.RawValues.OrderBy)) { parameters.Add("$orderby", oDataQuery.RawValues.OrderBy); } var parser = new ODataQueryOptionParser(edmModel, edmType, edmEntitySet, parameters); var queryable = (IQueryable <Developer>)_databaseContext.Developer; if (!string.IsNullOrWhiteSpace(oDataQuery.RawValues.Filter)) { var filter = new FilterQueryOption(oDataQuery.RawValues.Filter, context, parser); queryable = (IQueryable <Developer>)filter.ApplyTo(queryable, new ODataQuerySettings()); } if (!string.IsNullOrWhiteSpace(oDataQuery.RawValues.OrderBy)) { var orderBy = new OrderByQueryOption(oDataQuery.RawValues.OrderBy, context, parser); queryable = orderBy.ApplyTo(queryable, new ODataQuerySettings()); } IQueryable <object> expandableQueryable = null; if (!string.IsNullOrWhiteSpace(oDataQuery.RawValues.Expand)) { var expand = new SelectExpandQueryOption(null, oDataQuery.RawValues.Expand, context, parser); expandableQueryable = (IQueryable <object>)expand.ApplyTo(queryable, new ODataQuerySettings()); } int pageIndex = 1; var hasPageIndex = oDataQuery.Request.Query.TryGetValue("$pageindex", out StringValues pageIndexRaw); if (hasPageIndex) { var isTrue = int.TryParse(pageIndexRaw, out int _pageIndexRaw); pageIndex = (isTrue && _pageIndexRaw > 0) ? _pageIndexRaw : pageIndex; } int pageSize = 10; var hasPageSize = oDataQuery.Request.Query.TryGetValue("$pagesize", out StringValues pageSizeRaw); if (hasPageSize) { var isTrue = int.TryParse(pageSizeRaw, out int _pageSizeRaw); pageSize = (isTrue && _pageSizeRaw > 0) ? _pageSizeRaw : pageSize; } IQueryable <object> queryToExecute = expandableQueryable ?? queryable; var records = await queryToExecute.Skip((pageIndex - 1) *pageSize).Take(pageSize).ToListAsync(); var count = await queryToExecute.CountAsync(); var pageList = new PageList <Developer>(MapDomain(records).ToList(), count, pageIndex, pageSize); var response = PageListDto <DeveloperDto> .Map(pageList, DeveloperDto.MapDomainToDto); return(Ok(response)); }
/// <summary> /// Validates an <see cref="OrderByQueryOption" />. /// </summary> /// <param name="orderByOption">The $orderby query.</param> /// <param name="validationSettings">The validation settings.</param> public virtual void Validate(OrderByQueryOption orderByOption, ODataValidationSettings validationSettings) { if (orderByOption == null) { throw Error.ArgumentNull("orderByOption"); } if (validationSettings == null) { throw Error.ArgumentNull("validationSettings"); } int nodeCount = 0; for (OrderByClause clause = orderByOption.OrderByClause; clause != null; clause = clause.ThenBy) { nodeCount++; if (nodeCount > validationSettings.MaxOrderByNodeCount) { throw new ODataException(Error.Format(SRResources.OrderByNodeCountExceeded, validationSettings.MaxOrderByNodeCount)); } } bool enableOrderBy = orderByOption.Context.DefaultQuerySettings.EnableOrderBy; OrderByModelLimitationsValidator validator = new OrderByModelLimitationsValidator(orderByOption.Context, enableOrderBy); bool explicitAllowedProperties = validationSettings.AllowedOrderByProperties.Count > 0; foreach (OrderByNode node in orderByOption.OrderByNodes) { string propertyName = null; OrderByPropertyNode propertyNode = node as OrderByPropertyNode; if (propertyNode != null) { propertyName = propertyNode.Property.Name; bool isValidPath = !validator.TryValidate(propertyNode.OrderByClause, explicitAllowedProperties); if (propertyName != null && isValidPath && explicitAllowedProperties) { // Explicit allowed properties were specified, but this one isn't within the list of allowed // properties. if (!IsAllowed(validationSettings, propertyName)) { throw new ODataException(Error.Format(SRResources.NotAllowedOrderByProperty, propertyName, "AllowedOrderByProperties")); } } else if (propertyName != null) { // The property wasn't limited but it wasn't contained in the set of explicitly allowed // properties. if (!IsAllowed(validationSettings, propertyName)) { throw new ODataException(Error.Format(SRResources.NotAllowedOrderByProperty, propertyName, "AllowedOrderByProperties")); } } } else { propertyName = "$it"; if (!IsAllowed(validationSettings, propertyName)) { throw new ODataException(Error.Format(SRResources.NotAllowedOrderByProperty, propertyName, "AllowedOrderByProperties")); } } } }
// GET: odata/MyEntities //[EnableQuery] //public IQueryable<MyEntity> GetMyEntities(ODataQueryOptions opts) //{ // IQueryable results = opts.ApplyTo(db.MyEntities.AsQueryable()); // return results as IQueryable<MyEntity>; //} // GET: odata/MyEntities //[EnableQuery] public IQueryable <MyEntity> GetMyEntities(ODataQueryOptions opts) { var settings = new ODataValidationSettings() { // Initialize settings as needed. AllowedFunctions = AllowedFunctions.AllMathFunctions }; opts.Validate(settings); //IQueryable results = db.MyEntities.AsQueryable<MyEntity>(); //if (opts.Filter != null) //{ // results = opts.Filter.ApplyTo(results, new ODataQuerySettings() { EnableConstantParameterization = false, EnsureStableOrdering = false }); //} //results = results.Decompile(); //results.Decompile //IQueryable results = MyApplyToWithDecompile(db.MyEntities.AsQueryable(), opts); //return results as IQueryable<MyEntity>; //return db.MyEntities; IQueryable result = db.MyEntities.AsQueryable(); ODataQuerySettings querySettings = new ODataQuerySettings() { EnableConstantParameterization = false, EnsureStableOrdering = false }; // Construct the actual query and apply them in the following order: filter, orderby, skip, top if (opts.Filter != null) { result = opts.Filter.ApplyTo(result, querySettings); result = ((result as IQueryable <MyEntity>).Decompile()).AsQueryable(); } if (opts.InlineCount != null && Request.ODataProperties().TotalCount == null) { long?count = opts.InlineCount.GetEntityCount(result); if (count.HasValue) { Request.ODataProperties().TotalCount = count.Value; } } OrderByQueryOption orderBy = opts.OrderBy; // $skip or $top require a stable sort for predictable results. // Result limits require a stable sort to be able to generate a next page link. // If either is present in the query and we have permission, // generate an $orderby that will produce a stable sort. if (querySettings.EnsureStableOrdering && (opts.Skip != null || opts.Top != null || querySettings.PageSize.HasValue)) { // If there is no OrderBy present, we manufacture a default. // If an OrderBy is already present, we add any missing // properties necessary to make a stable sort. // Instead of failing early here if we cannot generate the OrderBy, // let the IQueryable backend fail (if it has to). orderBy = orderBy == null ? GenerateDefaultOrderBy(opts.Context) : EnsureStableSortOrderBy(orderBy, opts.Context); } if (orderBy != null) { result = orderBy.ApplyTo(result, querySettings); } if (opts.Skip != null) { result = opts.Skip.ApplyTo(result, querySettings); } if (opts.Top != null) { result = opts.Top.ApplyTo(result, querySettings); } if (opts.SelectExpand != null) { Request.ODataProperties().SelectExpandClause = opts.SelectExpand.SelectExpandClause; result = opts.SelectExpand.ApplyTo(result, querySettings); } if (querySettings.PageSize.HasValue) { bool resultsLimited; result = LimitResults(result, querySettings.PageSize.Value, out resultsLimited); if (resultsLimited && Request.RequestUri != null && Request.RequestUri.IsAbsoluteUri && Request.ODataProperties().NextLink == null) { Uri nextPageLink = GetNextPageLink(Request, querySettings.PageSize.Value); Request.ODataProperties().NextLink = nextPageLink; } } return(result as IQueryable <MyEntity>); // this compiles and works in most cases except when using $top with anything else. Even if no results are found. //return result.AsQueryable(); // this doesn't compile, Error: Cannot implicitly convert type 'System.Linq.IQueryable' to 'System.Linq.IQueryable<MyEntity>. An explicit conversion exists (are you missing a cast?) }
public void ApplyTo_WithUnTypedContext_Throws_InvalidOperation() { // Arrange CustomersModelWithInheritance model = new CustomersModelWithInheritance(); ODataQueryContext context = new ODataQueryContext(model.Model, model.Customer); OrderByQueryOption orderBy = new OrderByQueryOption("ID desc", context); IQueryable queryable = new Mock<IQueryable>().Object; // Act & Assert Assert.Throws<NotSupportedException>(() => orderBy.ApplyTo(queryable), "The query option is not bound to any CLR type. 'ApplyTo' is only supported with a query option bound to a CLR type."); }
public void CanApplyOrderByDescThenByDesc() { var model = new ODataModelBuilder().Add_Customer_EntityType().Add_Customers_EntitySet().GetServiceModel(); var orderByOption = new OrderByQueryOption("Name desc,Website desc", new ODataQueryContext(model, typeof(Customer))); var customers = (new List<Customer>{ new Customer { CustomerId = 1, Name = "ACME", Website = "http://www.acme.net" }, new Customer { CustomerId = 2, Name = "AAAA", Website = "http://www.aaaa.com" }, new Customer { CustomerId = 3, Name = "ACME", Website = "http://www.acme.com" } }).AsQueryable(); var results = orderByOption.ApplyTo(customers).ToArray(); Assert.Equal(1, results[0].CustomerId); Assert.Equal(3, results[1].CustomerId); Assert.Equal(2, results[2].CustomerId); }
public void CanApplyOrderBy_WithCollectionCount(string orderby) { // Arrange var model = new ODataModelBuilder() .Add_Order_EntityType() .Add_Customer_EntityType_With_Address() .Add_CustomerOrders_Relationship() .Add_Customer_EntityType_With_CollectionProperties() .Add_Customers_EntitySet() .GetEdmModel(); var parser = new ODataQueryOptionParser( model, model.FindType("Microsoft.AspNetCore.OData.Tests.Models.Customer"), model.FindDeclaredNavigationSource("Default.Container.Customers"), new Dictionary <string, string> { { "$orderby", orderby } }); var orderByOption = new OrderByQueryOption(orderby, new ODataQueryContext(model, typeof(Customer)), parser); var customers = (new List <Customer> { new Customer { Id = 1, Name = "Andy", Orders = new List <Order> { new Order { OrderId = 1 }, new Order { OrderId = 2 } }, Addresses = new List <Address> { new Address { City = "1" }, new Address { City = "2" } }, Aliases = new List <string> { "1", "2" } }, new Customer { Id = 2, Name = "Aaron", Orders = new List <Order> { new Order { OrderId = 3 } }, Addresses = new List <Address> { new Address { City = "3" } }, Aliases = new List <string> { "3" } }, new Customer { Id = 3, Name = "Alex" } }).AsQueryable(); // Act var results = orderByOption.ApplyTo(customers).ToArray(); // Assert Assert.Equal(3, results[0].Id); Assert.Equal(2, results[1].Id); Assert.Equal(1, results[2].Id); }
/// <summary> /// Updates the QueryExpression's sorting options. /// </summary> /// <param name="orders">The QueryExpression's collection of sorting options.</param> /// <param name="orderBy">The OData options for sorting.</param> private void UpdateOrdersFromOrderBy(DataCollection <OrderExpression> orders, OrderByQueryOption orderBy) { if (orderBy != null) { foreach (var node in orderBy.OrderByNodes.OfType <OrderByPropertyNode>()) { var attributeName = EntityMapper.GetAttributeName(node.Property.Name); var direction = node.Direction == OrderByDirection.Ascending ? OrderType.Ascending : OrderType.Descending; orders.Add(new OrderExpression(attributeName, direction)); } } }
public void CanApplyOrderBy() { var model = new ODataModelBuilder().Add_Customer_EntityType().Add_Customers_EntitySet().GetServiceModel(); var orderByOption = new OrderByQueryOption("Name", new ODataQueryContext(model, typeof(Customer))); var customers = (new List<Customer>{ new Customer { CustomerId = 1, Name = "Andy" }, new Customer { CustomerId = 2, Name = "Aaron" }, new Customer { CustomerId = 3, Name = "Alex" } }).AsQueryable(); var results = orderByOption.ApplyTo(customers).ToArray(); Assert.Equal(2, results[0].CustomerId); Assert.Equal(3, results[1].CustomerId); Assert.Equal(1, results[2].CustomerId); }
public static IAsyncEnumerable <T> ApplyTo <T>(this OrderByQueryOption query, IAsyncEnumerable <T> source, ODataQuerySettings settings) { return(source); // Not yet implemented. }