/// <summary> /// Apply the individual query to the given IQueryable in the right order. /// </summary> /// <param name="query">The IQueryable that we are applying query against.</param> /// <param name="handleNullPropagation">Specifies if we need to handle null propagation. Pass false if the underlying query provider handles null propagation. Otherwise pass true.</param> /// <param name="canUseDefaultOrderBy">If a default ordering can be used if the query doesn't specify one and has a $skip or $top.</param> /// <returns>The query that the query has been applied to.</returns> public IQueryable ApplyTo(IQueryable query, bool handleNullPropagation, bool canUseDefaultOrderBy) { if (query == null) { throw Error.ArgumentNull("query"); } IQueryable result = query; // Construct the actual query and apply them in the following order: filter, orderby, skip, top if (Filter != null) { result = Filter.ApplyTo(result, handleNullPropagation); } OrderByQueryOption orderBy = OrderBy; if (orderBy == null && (Skip != null || Top != null) && canUseDefaultOrderBy && !Context.IsPrimitiveClrType) { // Instead of failing early here if we cannot generate a default OrderBy, // let the IQueryable backend fail (if it has to). string orderByRaw = GenerateDefaultOrderBy(Context); if (!String.IsNullOrEmpty(orderByRaw)) { orderBy = new OrderByQueryOption(orderByRaw, Context); } } if (orderBy != null) { result = orderBy.ApplyTo(result); } if (Skip != null) { result = Skip.ApplyTo(result); } if (Top != null) { result = Top.ApplyTo(result); } return(result); }
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); }
public void ApplyTo_Does_Not_Replace_Original_OrderBy_With_Missing_Keys() { // Arrange var model = new ODataModelBuilder() .Add_Customers_No_Keys_EntitySet().GetEdmModel(); var message = new HttpRequestMessage( HttpMethod.Get, new Uri("http://server/service/Customers?$orderby=Name") ); // Act var queryOptions = new ODataQueryOptions(new ODataQueryContext(model, typeof(Customer)), message); OrderByQueryOption originalOption = queryOptions.OrderBy; ODataQuerySettings querySettings = new ODataQuerySettings(); IQueryable finalQuery = queryOptions.ApplyTo(new Customer[0].AsQueryable(), querySettings); // Assert Assert.ReferenceEquals(originalOption, queryOptions.OrderBy); }
public void CanApplyOrderBy() { var model = new ODataModelBuilder().Add_Customer_EntityType().Add_Customers_EntitySet().GetServiceModel(); var orderByOption = new OrderByQueryOption("Name", new ODataQueryContext(model, typeof(Customer), "Customers")); 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 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), "Customers")); 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 ApplyToEnums_ReturnsCorrectQueryable() { // Arrange var builder = new ODataConventionModelBuilder(); builder.EntitySet <EnumModel>("EnumModels"); var model = builder.GetEdmModel(); var context = new ODataQueryContext(model, typeof(EnumModel), "EnumModels"); var orderbyOption = new OrderByQueryOption("Flag", context); 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 CanApplySkipTopOrderby() { var model = new ODataModelBuilder().Add_Customer_EntityType().Add_Customers_EntitySet().GetServiceModel(); var context = new ODataQueryContext(model, typeof(Customer), "Customers"); 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); queryable = topOption.ApplyTo(queryable); var results = ((IQueryable <Customer>)queryable).ToArray(); Assert.Equal(2, results.Length); Assert.Equal(4, results[0].CustomerId); Assert.Equal(3, results[1].CustomerId); }
/// <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"); } if (request.GetConfiguration() != null) { _assembliesResolver = request.GetConfiguration().Services.GetAssembliesResolver(); } // fallback to the default assemblies resolver if none available. _assembliesResolver = _assembliesResolver ?? new DefaultAssembliesResolver(); // remember the context and request Context = context; Request = request; // 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; ThrowIfEmpty(kvp.Value, "$inlinecount"); InlineCount = new InlineCountQueryOption(kvp.Value, context); break; case "$expand": RawValues.Expand = kvp.Value; break; case "$format": RawValues.Format = kvp.Value; break; case "$skiptoken": RawValues.SkipToken = kvp.Value; break; default: // we don't throw if we can't recognize the query break; } } Validator = new ODataQueryValidator(); }
/// <summary> /// Apply the individual query to the given IQueryable in the right order. /// </summary> /// <param name="query">The original <see cref="IQueryable"/>.</param> /// <param name="querySettings">The settings to use in query composition.</param> /// <returns>The new <see cref="IQueryable"/> after the query has been applied to.</returns> public virtual IQueryable ApplyTo(IQueryable query, ODataQuerySettings querySettings) { if (query == null) { throw Error.ArgumentNull("query"); } if (querySettings == null) { throw Error.ArgumentNull("querySettings"); } IQueryable 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) { long?count = InlineCount.GetEntityCount(result); if (count.HasValue) { Request.SetInlineCount(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); } 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, Context, out resultsLimited); if (resultsLimited && Request.RequestUri != null && Request.RequestUri.IsAbsoluteUri) { Uri nextPageLink = GetNextPageLink(Request, querySettings.PageSize.Value); Request.SetNextPageLink(nextPageLink); } } return(result); }
/// <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; } } }