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));
        }
示例#2
0
        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);
        }
示例#3
0
        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);
        }
示例#4
0
        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);
        }
示例#6
0
        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));
        }
示例#7
0
        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);
        }
示例#8
0
        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);
        }
示例#10
0
        /// <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();
        }
示例#11
0
        /// <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);
        }
示例#12
0
        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" }
                }));
            }
        }
示例#13
0
        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);
        }
示例#14
0
        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);
        }
示例#15
0
 /// <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;
 }
示例#16
0
 /// <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));
 }