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 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 = ODataModelBuilderMocks.GetModelBuilderMock <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 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 CanApplyOrderBy_WithNestedParameterAlias() { // Arrange var model = new ODataModelBuilder().Add_Customer_EntityType().Add_Customers_EntitySet().GetServiceModel(); var parser = new ODataQueryOptionParser( model, model.FindType("System.Web.OData.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 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); 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[] { 5, 2, 1, 3, 6 }, actualCustomers.Select(enumModel => enumModel.Id)); }
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 CanApplyOrderByDescending() { var model = new ODataModelBuilder().Add_Customer_EntityType().Add_Customers_EntitySet().GetServiceModel(); var orderByOption = new OrderByQueryOption("Name desc", 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(1, results[0].CustomerId); Assert.Equal(3, results[1].CustomerId); Assert.Equal(2, results[2].CustomerId); }
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); }
/// <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": ThrowIfEmpty(kvp.Value, "$filter"); RawValues.Filter = kvp.Value; Filter = new FilterQueryOption(kvp.Value, context); break; case "$orderby": ThrowIfEmpty(kvp.Value, "$orderby"); RawValues.OrderBy = kvp.Value; OrderBy = new OrderByQueryOption(kvp.Value, context); break; case "$top": ThrowIfEmpty(kvp.Value, "$top"); RawValues.Top = kvp.Value; Top = new TopQueryOption(kvp.Value, context); break; case "$skip": ThrowIfEmpty(kvp.Value, "$skip"); RawValues.Skip = kvp.Value; Skip = new SkipQueryOption(kvp.Value, context); break; case "$select": RawValues.Select = kvp.Value; break; case "$count": ThrowIfEmpty(kvp.Value, "$count"); RawValues.Count = kvp.Value; Count = new CountQueryOption(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; } } if (RawValues.Select != null || RawValues.Expand != null) { SelectExpand = new SelectExpandQueryOption(RawValues.Select, RawValues.Expand, context); } 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 (Count != null && Request.ODataProperties().TotalCount == null) { long?count = Count.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 (SelectExpand != null) { Request.ODataProperties().SelectExpandClause = SelectExpand.SelectExpandClause; result = 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); }
private void BuildQueryOptions(IDictionary <string, string> queryParameters) { foreach (KeyValuePair <string, string> kvp in queryParameters) { switch (kvp.Key.ToLowerInvariant()) { case "$filter": ThrowIfEmpty(kvp.Value, "$filter"); RawValues.Filter = kvp.Value; Filter = new FilterQueryOption(kvp.Value, Context, _queryOptionParser); break; case "$orderby": ThrowIfEmpty(kvp.Value, "$orderby"); RawValues.OrderBy = kvp.Value; OrderBy = new OrderByQueryOption(kvp.Value, Context, _queryOptionParser); break; case "$top": ThrowIfEmpty(kvp.Value, "$top"); RawValues.Top = kvp.Value; Top = new TopQueryOption(kvp.Value, Context, _queryOptionParser); break; case "$skip": ThrowIfEmpty(kvp.Value, "$skip"); RawValues.Skip = kvp.Value; Skip = new SkipQueryOption(kvp.Value, Context, _queryOptionParser); break; case "$select": RawValues.Select = kvp.Value; break; case "$count": ThrowIfEmpty(kvp.Value, "$count"); RawValues.Count = kvp.Value; Count = new CountQueryOption(kvp.Value, Context, _queryOptionParser); break; case "$expand": RawValues.Expand = kvp.Value; break; case "$format": RawValues.Format = kvp.Value; break; case "$skiptoken": RawValues.SkipToken = kvp.Value; break; case "$deltatoken": RawValues.DeltaToken = kvp.Value; break; case "$apply": ThrowIfEmpty(kvp.Value, "$apply"); RawValues.Apply = kvp.Value; Apply = new ApplyQueryOption(kvp.Value, Context, _queryOptionParser); break; default: // we don't throw if we can't recognize the query break; } } if (RawValues.Select != null || RawValues.Expand != null) { SelectExpand = new SelectExpandQueryOption(RawValues.Select, RawValues.Expand, Context, _queryOptionParser); } if (ODataCountMediaTypeMapping.IsCountRequest(Request)) { Count = new CountQueryOption( "true", Context, new ODataQueryOptionParser( Context.Model, Context.ElementType, Context.NavigationSource, new Dictionary <string, string> { { "$count", "true" } })); } }
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; // First apply $apply // Section 3.15 of the spec http://docs.oasis-open.org/odata/odata-data-aggregation-ext/v4.0/cs01/odata-data-aggregation-ext-v4.0-cs01.html#_Toc378326311 if (IsAvailableODataQueryOption(Apply, AllowedQueryOptions.Apply)) { result = Apply.ApplyTo(result, querySettings, _assembliesResolver); Request.ODataProperties().ApplyClause = Apply.ApplyClause; this.Context.ElementClrType = Apply.ResultClrType; } // Construct the actual query and apply them in the following order: filter, orderby, skip, top if (IsAvailableODataQueryOption(Filter, AllowedQueryOptions.Filter)) { result = Filter.ApplyTo(result, querySettings, _assembliesResolver); } if (IsAvailableODataQueryOption(Count, AllowedQueryOptions.Count)) { if (Request.ODataProperties().TotalCountFunc == null) { Func <long> countFunc = Count.GetEntityCountFunc(result); if (countFunc != null) { Request.ODataProperties().TotalCountFunc = countFunc; } } if (ODataCountMediaTypeMapping.IsCountRequest(Request)) { return(result); } } 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 && (IsAvailableODataQueryOption(Skip, AllowedQueryOptions.Skip) || IsAvailableODataQueryOption(Top, AllowedQueryOptions.Top) || 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 (IsAvailableODataQueryOption(orderBy, AllowedQueryOptions.OrderBy)) { result = orderBy.ApplyTo(result, querySettings); } if (IsAvailableODataQueryOption(Skip, AllowedQueryOptions.Skip)) { result = Skip.ApplyTo(result, querySettings); } if (IsAvailableODataQueryOption(Top, AllowedQueryOptions.Top)) { result = Top.ApplyTo(result, querySettings); } AddAutoExpandProperties(querySettings); if (SelectExpand != null) { var tempResult = ApplySelectExpand(result, querySettings); if (tempResult != default(IQueryable)) { result = tempResult; } } 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 = Request.GetNextPageLink(querySettings.PageSize.Value); Request.ODataProperties().NextLink = nextPageLink; } } return(result); }
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("System.Web.OData.Builder.TestModels.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 { CustomerId = 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 { CustomerId = 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 { CustomerId = 3, Name = "Alex" } }).AsQueryable(); // Act var results = orderByOption.ApplyTo(customers).ToArray(); // Assert Assert.Equal(3, results[0].CustomerId); Assert.Equal(2, results[1].CustomerId); Assert.Equal(1, results[2].CustomerId); }
/// <summary> /// Создает экземпляр NewPlatform.Flexberry.ORM.ODataService.Expressions.OrderByQueryOption по экземпляру System.Web.OData.Query.OrderByQueryOption. /// </summary> /// <param name="orderByQueryOption">Экземпляр System.Web.OData.Query.OrderByQueryOption.</param> public OrderByQueryOption(System.Web.OData.Query.OrderByQueryOption orderByQueryOption, Type contextElementClrType) { Context = orderByQueryOption.Context; _contextElementClrType = contextElementClrType; OrderByNodes = orderByQueryOption.OrderByNodes; }
/// <summary> /// Creates a new instance of <see cref="OrderByQueryOption"/> based on the pased copy /// </summary> /// <param name="orderByQueryOption">An instance of <see cref="OrderByQueryOption"/> to use as a source for the new instnace's values</param> /// <returns>A new <see cref="OrderByQueryOption"/> instance</returns> protected virtual OrderByQueryOption CreateOrderByQueryOption(OrderByQueryOption orderByQueryOption) { return(new OrderByQueryOption(orderByQueryOption)); }