/// <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, HttpRequest request) { if (context == null) { throw Error.ArgumentNull("context"); } if (request == null) { throw Error.ArgumentNull("request"); } _assemblyProvider = request.AssemblyProvider(); Context = context; Request = request; RawValues = new ODataRawQueryOptions(); var queryOptionDict = request.Query.ToDictionary(p => p.Key, p => p.Value.FirstOrDefault()); _queryOptionParser = new ODataQueryOptionParser( context.Model, context.ElementType, context.NavigationSource, queryOptionDict); BuildQueryOptions(queryOptionDict); }
/// <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; } } }
/// <summary> /// Initializes a new instance of the <see cref="SelectExpandQueryOption"/> class. /// </summary> /// <param name="select">The $select query parameter value.</param> /// <param name="expand">The $expand query parameter value.</param> /// <param name="context">The <see cref="ODataQueryContext"/> which contains the <see cref="IEdmModel"/> and some type information.</param> /// <param name="queryOptionParser">The <see cref="ODataQueryOptionParser"/> which is used to parse the query option.</param> public SelectExpandQueryOption(string select, string expand, ODataQueryContext context, ODataQueryOptionParser queryOptionParser) { if (context == null) { throw Error.ArgumentNull("context"); } if (String.IsNullOrEmpty(select) && String.IsNullOrEmpty(expand)) { throw Error.Argument(SRResources.SelectExpandEmptyOrNull); } if (queryOptionParser == null) { throw Error.ArgumentNull("queryOptionParser"); } IEdmEntityType entityType = context.ElementType as IEdmEntityType; if (entityType == null) { throw Error.Argument("context", SRResources.SelectNonEntity, context.ElementType.ToTraceString()); } Context = context; RawSelect = select; RawExpand = expand; Validator = new SelectExpandQueryValidator(); _queryOptionParser = queryOptionParser; }
// This constructor is intended for unit testing only. internal SelectExpandQueryOption(string select, string expand, ODataQueryContext context) { if (context == null) { throw Error.ArgumentNull("context"); } if (String.IsNullOrEmpty(select) && String.IsNullOrEmpty(expand)) { throw Error.Argument(SRResources.SelectExpandEmptyOrNull); } IEdmEntityType entityType = context.ElementType as IEdmEntityType; if (entityType == null) { throw Error.Argument("context", SRResources.SelectNonEntity, context.ElementType.ToTraceString()); } Context = context; RawSelect = select; RawExpand = expand; Validator = new SelectExpandQueryValidator(); _queryOptionParser = new ODataQueryOptionParser( context.Model, context.ElementType, context.NavigationSource, new Dictionary<string, string> { { "$select", select }, { "$expand", expand } }); }
public void Validate_DepthChecks_DollarLevels(string expand, int maxExpansionDepth) { // Arrange var validator = new SelectExpandQueryValidator(); var builder = new ODataConventionModelBuilder(); builder.EntitySet<ODataLevelsTest.LevelsEntity>("Entities"); IEdmModel model = builder.GetEdmModel(); var context = new ODataQueryContext(model, typeof(ODataLevelsTest.LevelsEntity)); var selectExpandQueryOption = new SelectExpandQueryOption(null, expand, context); selectExpandQueryOption.LevelsMaxLiteralExpansionDepth = 1; // Act & Assert Assert.Throws<ODataException>( () => validator.Validate( selectExpandQueryOption, new ODataValidationSettings { MaxExpansionDepth = maxExpansionDepth }), String.Format( CultureInfo.CurrentCulture, "The request includes a $expand path which is too deep. The maximum depth allowed is {0}. " + "To increase the limit, set the 'MaxExpansionDepth' property on EnableQueryAttribute or ODataValidationSettings.", maxExpansionDepth)); Assert.DoesNotThrow( () => validator.Validate( selectExpandQueryOption, new ODataValidationSettings { MaxExpansionDepth = maxExpansionDepth + 1 })); }
public ODataQueryValidatorTest() { _validator = new ODataQueryValidator(); _builder = new ODataConventionModelBuilder(); _builder.EntitySet<QueryCompositionCustomer>("Customer"); _model = _builder.GetEdmModel(); _context = new ODataQueryContext(_model, typeof(QueryCompositionCustomer)); }
public void Value_Returns_ParsedSkipValue(string skipValue, int expectedValue) { var model = new ODataModelBuilder().Add_Customer_EntityType().Add_Customers_EntitySet().GetEdmModel(); var context = new ODataQueryContext(model, typeof(Customer)); var skip = new SkipQueryOption(skipValue, context); Assert.Equal(expectedValue, skip.Value); }
public void Constructor_TakingClrType_WithPrimitiveTypes(Type type) { // Arrange & Act ODataQueryContext context = new ODataQueryContext(EdmCoreModel.Instance, type); // Assert Assert.True(context.ElementClrType == type); }
public virtual object ApplyQueryOptions(object value, HttpRequest request, ActionDescriptor actionDescriptor, AssembliesResolver assembliesResolver) { var elementClrType = value is IEnumerable ? TypeHelper.GetImplementedIEnumerableType(value.GetType()) : value.GetType(); var model = request.ODataProperties().Model; if (model == null) { throw Error.InvalidOperation(SRResources.QueryGetModelMustNotReturnNull); } var queryContext = new ODataQueryContext( model, elementClrType, assembliesResolver, request.ODataProperties().Path ); var queryOptions = new ODataQueryOptions(queryContext, request, assembliesResolver); var enumerable = value as IEnumerable; if (enumerable == null) { // response is single entity. return value; } // response is a collection. var query = (value as IQueryable) ?? enumerable.AsQueryable(); query = queryOptions.ApplyTo(query, new ODataQuerySettings { // TODO: If we are using SQL, set this to false // otherwise if it is entities in code then // set it to true HandleNullPropagation = //HandleNullPropagationOption.True HandleNullPropagationOptionHelper.GetDefaultHandleNullPropagationOption(query), PageSize = actionDescriptor.PageSize(), SearchDerivedTypeWhenAutoExpand = true }, AllowedQueryOptions.None); // Determine if this result should be a single entity if (ODataCountMediaTypeMapping.IsCountRequest(request)) { long? count = request.ODataProperties().TotalCount; if (count.HasValue) { // Return the count value if it is a $count request. return count.Value; } } return query; }
internal SelectExpandQueryOption( string select, string expand, ODataQueryContext context, SelectExpandClause selectExpandClause) : this(select, expand, context) { _selectExpandClause = selectExpandClause; }
public void ApplyInValidSkipQueryThrows(string skipValue) { var model = new ODataModelBuilder().Add_Customer_EntityType().Add_Customers_EntitySet().GetEdmModel(); var context = new ODataQueryContext(model, typeof(Customer)); var skip = new SkipQueryOption(skipValue, context); Assert.Throws<ODataException>(() => skip.ApplyTo(ODataQueryOptionTest.Customers)); }
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); }
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 Ctor_ThrowsArgument_IfBothSelectAndExpandAreNull() { _model.Model.SetAnnotationValue<ClrTypeAnnotation>(_model.Customer, new ClrTypeAnnotation(typeof(Customer))); ODataQueryContext context = new ODataQueryContext(_model.Model, typeof(Customer)); Assert.Throws<ArgumentException>( () => new SelectExpandQueryOption(select: null, expand: null, context: context), "'select' and 'expand' cannot be both null or empty."); }
public void Ctor_ThrowsArgument_IfContextIsNotForAnEntityType() { ODataQueryContext context = new ODataQueryContext(_model.Model, typeof(int)); Assert.ThrowsArgument( () => new SelectExpandQueryOption(select: "Name", expand: "Name", context: context), "context", "The type 'Edm.Int32' is not an entity type. Only entity types support $select and $expand."); }
public void CanConstructValidFilterQuery(string filterValue) { var model = new ODataModelBuilder().Add_Customer_EntityType().Add_Customers_EntitySet().GetEdmModel(); var context = new ODataQueryContext(model, typeof(Customer), "Customers"); var filter = new FilterQueryOption(filterValue, context); Assert.Same(context, filter.Context); Assert.Equal(filterValue, filter.RawValue); }
public void CanConstructValidFilterQuery(string skipValue) { var model = new ODataModelBuilder().Add_Customer_EntityType().Add_Customers_EntitySet().GetEdmModel(); var context = new ODataQueryContext(model, typeof(Customer)); var skip = new SkipQueryOption(skipValue, context); Assert.Same(context, skip.Context); Assert.Equal(skipValue, skip.RawValue); }
public void Ctor_Throws_Argument_IfContextIsUnTyped() { IEdmModel model = EdmCoreModel.Instance; IEdmType elementType = EdmCoreModel.Instance.GetPrimitiveType(EdmPrimitiveTypeKind.Int32); ODataQueryContext context = new ODataQueryContext(model, elementType); Assert.ThrowsArgument( () => new ODataQueryOptions<int>(context, new HttpRequestMessage()), "context", "The property 'ElementClrType' of ODataQueryContext cannot be null."); }
public void Ctor_SuccedsIfEntityTypesMatch() { ODataConventionModelBuilder builder = new ODataConventionModelBuilder(); builder.EntitySet<Customer>("Customers"); ODataQueryContext context = new ODataQueryContext(builder.GetEdmModel(), typeof(Customer)); ODataQueryOptions<Customer> query = new ODataQueryOptions<Customer>(context, new HttpRequestMessage(HttpMethod.Get, "http://server/?$top=10")); Assert.Equal("10", query.Top.RawValue); }
public void Ctor_ThrowsArgumentNull_QueryOptionParser() { // Arrange ODataQueryContext context = new ODataQueryContext(_model.Model, typeof(int)); // Act & Assert Assert.ThrowsArgumentNull( () => new SelectExpandQueryOption("select", "expand", context, queryOptionParser: null), "queryOptionParser"); }
public void Ctor_Throws_Argument_IfContextIsofDifferentEntityType() { ODataConventionModelBuilder builder = new ODataConventionModelBuilder(); builder.EntitySet<Customer>("Customers"); ODataQueryContext context = new ODataQueryContext(builder.GetEdmModel(), typeof(Customer)); Assert.ThrowsArgument( () => new ODataQueryOptions<int>(context, new HttpRequestMessage()), "context", "The entity type 'System.Web.OData.TestCommon.Models.Customer' does not match the expected entity type 'System.Int32' as set on the query context."); }
public void ContructorWithOnlyType(Type type) { // Arrange & Act ODataQueryContext context = new ODataQueryContext(type); // Assert Assert.Null(context.Model); Assert.True(context.EntityClrType == type); Assert.Null(context.EntitySet); Assert.True(context.IsPrimitiveClrType); }
public void ApplyTo_WithUnTypedContext_Throws_InvalidOperation() { // Arrange CustomersModelWithInheritance model = new CustomersModelWithInheritance(); ODataQueryContext context = new ODataQueryContext(model.Model, model.Customer); SkipQueryOption skip = new SkipQueryOption("42", context); IQueryable queryable = new Mock<IQueryable>().Object; // Act & Assert Assert.Throws<NotSupportedException>(() => skip.ApplyTo(queryable, new ODataQuerySettings()), "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 ApplyTo_OnSingleEntity_WithUnTypedContext_Throws_InvalidOperation() { // Arrange CustomersModelWithInheritance model = new CustomersModelWithInheritance(); ODataQueryContext context = new ODataQueryContext(model.Model, model.Customer); SelectExpandQueryOption selectExpand = new SelectExpandQueryOption(select: "ID", expand: null, context: context); object entity = new object(); // Act & Assert Assert.Throws<NotSupportedException>(() => selectExpand.ApplyTo(entity, new ODataQuerySettings()), "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 ApplyTo_OnQueryable_WithUnTypedContext_Throws_InvalidOperation() { // Arrange CustomersModelWithInheritance model = new CustomersModelWithInheritance(); ODataQueryContext context = new ODataQueryContext(model.Model, model.Customer); SelectExpandQueryOption selectExpand = new SelectExpandQueryOption(select: "ID", expand: null, context: context, queryTranslator: null); IQueryable queryable = new Mock<IQueryable>().Object; // Act & Assert Assert.Throws<NotSupportedException>(() => selectExpand.ApplyTo(queryable, new ODataQuerySettings()), "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 ApplyTo_Succeeds_If_QueryTypeMatchesOptionsType() { ODataConventionModelBuilder builder = new ODataConventionModelBuilder(); builder.EntitySet<Customer>("Customers"); ODataQueryContext context = new ODataQueryContext(builder.GetEdmModel(), typeof(Customer)); ODataQueryOptions<Customer> query = new ODataQueryOptions<Customer>(context, new HttpRequestMessage(HttpMethod.Get, "http://server/?$top=10")); Assert.DoesNotThrow( () => query.ApplyTo(Enumerable.Empty<Customer>().AsQueryable())); }
public void CanConstructValidFilterQuery(string topValue) { // Arrange var model = new ODataModelBuilder().Add_Customer_EntityType().Add_Customers_EntitySet().GetEdmModel(); var context = new ODataQueryContext(model, typeof(Customer)); // Act var top = new TopQueryOption(topValue, context); // Assert Assert.Same(context, top.Context); Assert.Equal(topValue, top.RawValue); }
public void Ctor_SetsProperty_RawSelect() { // Arrange string selectValue = "select"; _model.Model.SetAnnotationValue<ClrTypeAnnotation>(_model.Customer, new ClrTypeAnnotation(typeof(Customer))); ODataQueryContext context = new ODataQueryContext(_model.Model, typeof(Customer)); // Act SelectExpandQueryOption result = new SelectExpandQueryOption(selectValue, expand: null, context: context, queryTranslator: null); // Assert Assert.Equal(selectValue, result.RawSelect); }
public void Ctor_SetsProperty_RawExpand() { // Arrange string expandValue = "expand"; _model.Model.SetAnnotationValue<ClrTypeAnnotation>(_model.Customer, new ClrTypeAnnotation(typeof(Customer))); ODataQueryContext context = new ODataQueryContext(_model.Model, typeof(Customer)); // Act SelectExpandQueryOption result = new SelectExpandQueryOption(select: null, expand: expandValue, context: context); // Assert Assert.Equal(expandValue, result.RawExpand); }
public void ApplyTo_ThrowsInvalidOp_If_QueryTypeDoesnotMatch() { ODataConventionModelBuilder builder = new ODataConventionModelBuilder(); builder.EntitySet<Customer>("Customers"); ODataQueryContext context = new ODataQueryContext(builder.GetEdmModel(), typeof(Customer)); ODataQueryOptions<Customer> query = new ODataQueryOptions<Customer>(context, new HttpRequestMessage(HttpMethod.Get, "http://server/?$top=10")); Assert.Throws<InvalidOperationException>( () => query.ApplyTo(Enumerable.Empty<int>().AsQueryable()), "Cannot apply ODataQueryOptions of 'System.Web.Http.OData.TestCommon.Models.Customer' to IQueryable of 'System.Int32'."); }
// Returns a sorted list of all properties that may legally appear // in an OrderBy. If the entity type has keys, all are returned. // Otherwise, when no keys are present, all primitive properties are returned. private static IEnumerable <IEdmStructuralProperty> GetAvailableOrderByProperties(ODataQueryContext context) { Contract.Assert(context != null); var entityType = context.ElementType as IEdmEntityType; if (entityType == null) { return(Enumerable.Empty <IEdmStructuralProperty>()); } var properties = entityType.Key().Any() ? entityType.Key() : entityType .StructuralProperties() .Where(property => property.Type.IsPrimitive() && !property.Type.IsStream()); return(properties.OrderBy(o => { var value = o.DeclaringType as PrimitivePropertyConfiguration; return value == null ? 0 : value.Order; }).ThenBy(o => o.Name).ToList()); }
public TopQueryValidatorTest() { _validator = new TopQueryValidator(); _context = ValidationTestHelper.CreateCustomerContext(); }
/// <summary> /// Ensures the given <see cref="OrderByQueryOption"/> will produce a stable sort. /// If it will, the input <paramref name="orderBy"/> will be returned /// unmodified. If the given <see cref="OrderByQueryOption"/> will not produce a /// stable sort, a new <see cref="OrderByQueryOption"/> instance will be created /// and returned. /// </summary> /// <param name="orderBy">The <see cref="OrderByQueryOption"/> to evaluate.</param> /// <param name="context">The <see cref="ODataQueryContext"/>.</param> /// <param name="applySortOptions"></param> /// <returns>An <see cref="OrderByQueryOption"/> that will produce a stable sort.</returns> private OrderByQueryOption EnsureStableSortOrderBy(OrderByQueryOption orderBy, ODataQueryContext context, List <string> applySortOptions) { Contract.Assert(orderBy != null); Contract.Assert(context != null); // Strategy: create a hash of all properties already used in the given OrderBy // and remove them from the list of properties we need to add to make the sort stable. Func <OrderByPropertyNode, string> propertyFunc = null; if (applySortOptions != null) { propertyFunc = node => node.PropertyPath; } else { propertyFunc = node => node.Property.Name; } HashSet <string> usedPropertyNames = new HashSet <string>(orderBy.OrderByNodes.OfType <OrderByPropertyNode>().Select(propertyFunc)); if (applySortOptions != null) { var propertyPathsToAdd = applySortOptions.Where(p => !usedPropertyNames.Contains(p)).OrderBy(p => p); if (propertyPathsToAdd.Any()) { var orderByRaw = orderBy.RawValue + "," + string.Join(",", propertyPathsToAdd); orderBy = new OrderByQueryOption(orderByRaw, context, Apply.RawValue); } } else { IEnumerable <IEdmStructuralProperty> propertiesToAdd = GetAvailableOrderByProperties(context).Where(prop => !usedPropertyNames.Contains(prop.Name)); if (propertiesToAdd.Any()) { // The existing query options has too few properties to create a stable sort. // Clone the given one and add the remaining properties to end, thereby making // the sort stable but preserving the user's original intent for the major // sort order. orderBy = new OrderByQueryOption(orderBy); foreach (IEdmStructuralProperty property in propertiesToAdd) { orderBy.OrderByNodes.Add(new OrderByPropertyNode(property, OrderByDirection.Ascending)); } } } return(orderBy); }
public void ProcessLevelsCorrectly_NotAllSelected() { // Arrange var model = ODataLevelsTest.GetEdmModel(); var context = new ODataQueryContext( model, model.FindDeclaredType("Microsoft.AspNet.OData.Test.Routing.LevelsEntity")); context.RequestContainer = new MockContainer(); var selectExpand = new SelectExpandQueryOption( select: "Name", expand: "Parent($select=ID;$levels=max)", context: context); // Act SelectExpandClause clause = selectExpand.ProcessLevels(); // Assert // Level 1. Assert.False(clause.AllSelected); Assert.Equal(2, clause.SelectedItems.Count()); var nameSelectItem = Assert.Single(clause.SelectedItems.OfType <PathSelectItem>().Where( item => item.SelectedPath.FirstSegment is PropertySegment)); Assert.Equal("Name", ((PropertySegment)nameSelectItem.SelectedPath.FirstSegment).Property.Name); // Before ODL 7.6, the expand navigation property will be added as a select item (PathSelectItem). // After ODL 7.6 (include 7.6), the expand navigation property will not be added. // Comment the following codes for visibility later. /* * var parentSelectItem = Assert.Single(clause.SelectedItems.OfType<PathSelectItem>().Where( * item => item.SelectedPath.FirstSegment is NavigationPropertySegment)); * Assert.Equal( * "Parent", * ((NavigationPropertySegment)parentSelectItem.SelectedPath.FirstSegment).NavigationProperty.Name); */ Assert.Empty(clause.SelectedItems.OfType <PathSelectItem>().Where(item => item.SelectedPath.FirstSegment is NavigationPropertySegment)); var expandedItem = Assert.Single(clause.SelectedItems.OfType <ExpandedNavigationSelectItem>()); Assert.Equal( "Parent", ((NavigationPropertySegment)expandedItem.PathToNavigationProperty.FirstSegment).NavigationProperty.Name); Assert.Null(expandedItem.LevelsOption); // Level 2. clause = expandedItem.SelectAndExpand; Assert.False(clause.AllSelected); Assert.Equal(3, clause.SelectedItems.Count()); var idSelectItem = Assert.Single(clause.SelectedItems.OfType <PathSelectItem>().Where( item => item.SelectedPath.FirstSegment is PropertySegment)); Assert.Equal("ID", ((PropertySegment)idSelectItem.SelectedPath.FirstSegment).Property.Name); var parentSelectItem = Assert.Single(clause.SelectedItems.OfType <PathSelectItem>().Where( item => item.SelectedPath.FirstSegment is NavigationPropertySegment)); Assert.Equal( "Parent", ((NavigationPropertySegment)parentSelectItem.SelectedPath.FirstSegment).NavigationProperty.Name); expandedItem = Assert.Single(clause.SelectedItems.OfType <ExpandedNavigationSelectItem>()); Assert.Equal( "Parent", ((NavigationPropertySegment)expandedItem.PathToNavigationProperty.FirstSegment).NavigationProperty.Name); Assert.Null(expandedItem.LevelsOption); clause = expandedItem.SelectAndExpand; Assert.False(clause.AllSelected); Assert.Single(clause.SelectedItems); idSelectItem = Assert.IsType <PathSelectItem>(clause.SelectedItems.Single()); Assert.Equal("ID", ((PropertySegment)idSelectItem.SelectedPath.FirstSegment).Property.Name); }
public void ProcessLevelsCorrectly_WithMultipleProperties() { // Arrange var model = ODataLevelsTest.GetEdmModel(); var context = new ODataQueryContext( model, model.FindDeclaredType("Microsoft.AspNet.OData.Test.Routing.LevelsEntity")); context.RequestContainer = new MockContainer(); var selectExpand = new SelectExpandQueryOption( select: null, expand: "Parent($expand=Parent($levels=max),DerivedAncestors($levels=2;$select=ID)),BaseEntities($levels=2)", context: context); selectExpand.LevelsMaxLiteralExpansionDepth = 3; // Act SelectExpandClause clause = selectExpand.ProcessLevels(); // Assert Assert.True(clause.AllSelected); Assert.Equal(2, clause.SelectedItems.Count()); // Top level Parent. var parent = Assert.Single(clause.SelectedItems.OfType <ExpandedNavigationSelectItem>().Where( item => item.PathToNavigationProperty.FirstSegment is NavigationPropertySegment && ((NavigationPropertySegment)item.PathToNavigationProperty.FirstSegment).NavigationProperty.Name == "Parent")); Assert.Null(parent.LevelsOption); var clauseOfParent = parent.SelectAndExpand; Assert.True(clauseOfParent.AllSelected); Assert.Equal(2, clauseOfParent.SelectedItems.Count()); // Level 1 of inline Parent. var inlineParent = Assert.Single(clauseOfParent.SelectedItems.OfType <ExpandedNavigationSelectItem>().Where( item => item.PathToNavigationProperty.FirstSegment is NavigationPropertySegment && ((NavigationPropertySegment)item.PathToNavigationProperty.FirstSegment).NavigationProperty.Name == "Parent")); Assert.Null(inlineParent.LevelsOption); // Level 2 of inline Parent. var inlineParentClause = inlineParent.SelectAndExpand; Assert.True(inlineParentClause.AllSelected); Assert.Single(inlineParentClause.SelectedItems); inlineParent = Assert.IsType <ExpandedNavigationSelectItem>(inlineParentClause.SelectedItems.Single()); Assert.Equal( "Parent", ((NavigationPropertySegment)inlineParent.PathToNavigationProperty.FirstSegment).NavigationProperty.Name); Assert.Null(inlineParent.LevelsOption); inlineParentClause = inlineParent.SelectAndExpand; Assert.True(inlineParentClause.AllSelected); Assert.Empty(inlineParentClause.SelectedItems); // Level 1 of inline DerivedAncestors. var inlineDerivedAncestors = Assert.Single(clauseOfParent.SelectedItems.OfType <ExpandedNavigationSelectItem>().Where( item => item.PathToNavigationProperty.FirstSegment is NavigationPropertySegment && ((NavigationPropertySegment)item.PathToNavigationProperty.FirstSegment).NavigationProperty.Name == "DerivedAncestors")); Assert.Null(inlineDerivedAncestors.LevelsOption); // Level 2 of inline DerivedAncestors. var inlineDerivedAncestorsClause = inlineDerivedAncestors.SelectAndExpand; Assert.False(inlineDerivedAncestorsClause.AllSelected); Assert.Equal(3, inlineDerivedAncestorsClause.SelectedItems.Count()); var idItem = Assert.Single(inlineDerivedAncestorsClause.SelectedItems.OfType <PathSelectItem>().Where( item => item.SelectedPath.FirstSegment is PropertySegment)); Assert.Equal("ID", ((PropertySegment)idItem.SelectedPath.FirstSegment).Property.Name); var derivedAncestorsItem = Assert.Single(inlineDerivedAncestorsClause.SelectedItems.OfType <PathSelectItem>().Where( item => item.SelectedPath.FirstSegment is NavigationPropertySegment)); Assert.Equal( "DerivedAncestors", ((NavigationPropertySegment)derivedAncestorsItem.SelectedPath.FirstSegment).NavigationProperty.Name); inlineDerivedAncestors = Assert.Single(inlineDerivedAncestorsClause.SelectedItems.OfType <ExpandedNavigationSelectItem>()); Assert.Equal( "DerivedAncestors", ((NavigationPropertySegment)inlineDerivedAncestors.PathToNavigationProperty.FirstSegment).NavigationProperty.Name); Assert.Null(inlineDerivedAncestors.LevelsOption); inlineDerivedAncestorsClause = inlineDerivedAncestors.SelectAndExpand; Assert.False(inlineDerivedAncestorsClause.AllSelected); Assert.Single(inlineDerivedAncestorsClause.SelectedItems); idItem = Assert.Single(inlineDerivedAncestorsClause.SelectedItems.OfType <PathSelectItem>()); Assert.Equal("ID", ((PropertySegment)idItem.SelectedPath.FirstSegment).Property.Name); // Level 1 of BaseEntities. var baseEntities = Assert.Single(clause.SelectedItems.OfType <ExpandedNavigationSelectItem>().Where( item => item.PathToNavigationProperty.FirstSegment is NavigationPropertySegment && ((NavigationPropertySegment)item.PathToNavigationProperty.FirstSegment).NavigationProperty.Name == "BaseEntities")); Assert.Null(baseEntities.LevelsOption); // Level 2 of BaseEntities. var baseEntitiesClause = baseEntities.SelectAndExpand; Assert.True(baseEntitiesClause.AllSelected); Assert.Single(baseEntitiesClause.SelectedItems); baseEntities = Assert.IsType <ExpandedNavigationSelectItem>(baseEntitiesClause.SelectedItems.Single()); Assert.Equal( "BaseEntities", ((NavigationPropertySegment)baseEntities.PathToNavigationProperty.FirstSegment).NavigationProperty.Name); Assert.Null(baseEntities.LevelsOption); baseEntitiesClause = baseEntities.SelectAndExpand; Assert.True(baseEntitiesClause.AllSelected); Assert.Empty(baseEntitiesClause.SelectedItems); }
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, DevCode>(MapDomain(records).ToList(), count, pageIndex, pageSize); var response = PageListDto <DeveloperDto> .Map(pageList, DeveloperDto.MapDomainToDto); return(Ok(response)); }
public SearchODataQueryOptions(ODataQueryContext context, HttpRequestMessage request) : base(context, request) { BuildQueryOptions(request.GetQueryNameValuePairs()); }
/// <summary> /// Ensures the given <see cref="OrderByQueryOption"/> will produce a stable sort. /// If it will, the input <paramref name="orderBy"/> will be returned /// unmodified. If the given <see cref="OrderByQueryOption"/> will not produce a /// stable sort, a new <see cref="OrderByQueryOption"/> instance will be created /// and returned. /// </summary> /// <param name="orderBy">The <see cref="OrderByQueryOption"/> to evaluate.</param> /// <param name="context">The <see cref="ODataQueryContext"/>.</param> /// <returns>An <see cref="OrderByQueryOption"/> that will produce a stable sort.</returns> private static OrderByQueryOption EnsureStableSortOrderBy(OrderByQueryOption orderBy, ODataQueryContext context) { Contract.Assert(orderBy != null); Contract.Assert(context != null); // Strategy: create a hash of all properties already used in the given OrderBy // and remove them from the list of properties we need to add to make the sort stable. HashSet <string> usedPropertyNames = new HashSet <string>(orderBy.OrderByNodes.OfType <OrderByPropertyNode>().Select(node => node.Property.Name)); IEnumerable <IEdmStructuralProperty> propertiesToAdd = GetAvailableOrderByProperties(context).Where(prop => !usedPropertyNames.Contains(prop.Name)); if (propertiesToAdd.Any()) { // The existing query options has too few properties to create a stable sort. // Clone the given one and add the remaining properties to end, thereby making // the sort stable but preserving the user's original intent for the major // sort order. orderBy = new OrderByQueryOption(orderBy); foreach (IEdmStructuralProperty property in propertiesToAdd) { orderBy.OrderByNodes.Add(new OrderByPropertyNode(property, OrderByDirection.Ascending)); } } return(orderBy); }
// Returns a sorted list of all properties that may legally appear // in an OrderBy. If the entity type has keys, all are returned. // Otherwise, when no keys are present, all primitive properties are returned. private static IEnumerable <IEdmStructuralProperty> GetAvailableOrderByProperties(ODataQueryContext context) { Contract.Assert(context != null); IEdmEntityType entityType = context.ElementType as IEdmEntityType; if (entityType != null) { IEnumerable <IEdmStructuralProperty> properties = entityType.Key().Any() ? entityType.Key() : entityType .StructuralProperties() .Where(property => property.Type.IsPrimitive()); // Sort properties alphabetically for stable sort return(properties.OrderBy(property => property.Name)); } else { return(Enumerable.Empty <IEdmStructuralProperty>()); } }
private static IDictionary <string, string> GetQueryParameters(HttpRequestMessage request, ODataQueryContext context) { IDictionary <string, string> parameters = request.GetQueryNameValuePairs().ToDictionary(p => p.Key, p => p.Value); IEdmEntityType entityType = context.ElementType as IEdmEntityType; if (entityType != null && parameters.ContainsKey("$select")) { var navigationProperties = entityType.NavigationProperties(); if (navigationProperties != null) { string autoExpandNavigationProperties = String.Empty; foreach (var navigationProperty in navigationProperties) { if (EdmLibHelpers.IsAutoExpand(navigationProperty, context.Model)) { if (!String.IsNullOrEmpty(autoExpandNavigationProperties)) { autoExpandNavigationProperties += ","; } autoExpandNavigationProperties += navigationProperty.Name; } } if (!String.IsNullOrEmpty(autoExpandNavigationProperties)) { if (parameters.ContainsKey("$expand")) { parameters["$expand"] += "," + autoExpandNavigationProperties; } else { parameters["$expand"] = autoExpandNavigationProperties; } } } } return(parameters); }
public void ApplyTo_NestedProperties_WithDuplicatePathType_Succeeds() { // Arrange var model = new ODataModelBuilder().Add_Customer_EntityType_With_DuplicatedAddress() .Add_Customers_EntitySet() .GetServiceModel(); var context = new ODataQueryContext(model, typeof(Customer)) { RequestContainer = new MockContainer() }; var orderByOption = new OrderByQueryOption("City,Address/City,WorkAddress/City", context); var customers = (new List <Customer> { new Customer { CustomerId = 1, City = "B", Address = new Address { City = "B" }, WorkAddress = new Address { City = "B" } }, new Customer { CustomerId = 2, City = "B", Address = new Address { City = "B" }, WorkAddress = new Address { City = "A" } }, new Customer { CustomerId = 3, City = "B", Address = new Address { City = "A" }, WorkAddress = new Address { City = "A" } }, new Customer { CustomerId = 4, City = "A", Address = new Address { City = "A" }, WorkAddress = new Address { City = "A" } } }).AsQueryable(); // Act var results = orderByOption.ApplyTo(customers).ToArray(); // Assert Assert.Equal(4, results[0].CustomerId); Assert.Equal(3, results[1].CustomerId); Assert.Equal(2, results[2].CustomerId); Assert.Equal(1, results[3].CustomerId); }
public ChildCollectionOrderByUpdater(List <ODataExpansionOptions> expansions, ODataQueryContext context) : base(expansions) { this.context = context; }
/// <summary> /// Creates a new instance of the <see cref="CountQueryOption" /> class. /// </summary> /// <param name="rawValue">The raw value for the $count query option.</param> /// <param name="context">The <see cref="ODataQueryContext"/> which contains the query context.</param> /// <param name="queryOptionParser">The <see cref="ODataQueryOptionParser"/> which is used to parse the query option.</param> /// <returns>A new <see cref="CountQueryOption" /> instance</returns> protected virtual CountQueryOption CreateCountQueryOption(string rawValue, ODataQueryContext context, ODataQueryOptionParser queryOptionParser) { return(new CountQueryOption(rawValue, context, queryOptionParser)); }
public void ProcessLevelsCorrectly_WithAutoExpand(string url) { // Arrange var model = GetAutoExpandEdmModel(); var context = new ODataQueryContext( model, model.FindDeclaredType("Microsoft.AspNet.OData.Test.Common.Models.AutoExpandCustomer")); var request = RequestFactory.Create(HttpMethod.Get, url); var queryOption = new ODataQueryOptions(context, request); queryOption.AddAutoSelectExpandProperties(); var selectExpand = queryOption.SelectExpand; // Act SelectExpandClause clause = selectExpand.ProcessLevels(); // Assert Assert.True(clause.AllSelected); Assert.Equal(2, clause.SelectedItems.Count()); // Level 1 of Customer. var cutomer = Assert.Single( clause.SelectedItems.OfType <ExpandedNavigationSelectItem>().Where( item => item.PathToNavigationProperty.FirstSegment is NavigationPropertySegment && ((NavigationPropertySegment)item.PathToNavigationProperty.FirstSegment).NavigationProperty .Name == "Friend") ); var clauseOfCustomer = cutomer.SelectAndExpand; Assert.True(clauseOfCustomer.AllSelected); Assert.Equal(2, clauseOfCustomer.SelectedItems.Count()); // Order under Customer. var order = Assert.Single( clause.SelectedItems.OfType <ExpandedNavigationSelectItem>().Where( item => item.PathToNavigationProperty.FirstSegment is NavigationPropertySegment && ((NavigationPropertySegment)item.PathToNavigationProperty.FirstSegment).NavigationProperty .Name == "Order") ); Assert.Null(order.LevelsOption); var clauseOfOrder = order.SelectAndExpand; Assert.True(clauseOfOrder.AllSelected); Assert.Single(clauseOfOrder.SelectedItems); // Choice Order under Order var choiceOrder = Assert.IsType <ExpandedNavigationSelectItem>(clauseOfOrder.SelectedItems.Single()); Assert.Null(choiceOrder.LevelsOption); Assert.True(choiceOrder.SelectAndExpand.AllSelected); Assert.Empty(choiceOrder.SelectAndExpand.SelectedItems); // Level 2 of Order. order = Assert.Single( clauseOfCustomer.SelectedItems.OfType <ExpandedNavigationSelectItem>().Where( item => item.PathToNavigationProperty.FirstSegment is NavigationPropertySegment && ((NavigationPropertySegment)item.PathToNavigationProperty.FirstSegment).NavigationProperty .Name == "Order") ); Assert.Null(order.LevelsOption); clauseOfOrder = order.SelectAndExpand; Assert.True(clauseOfOrder.AllSelected); Assert.Empty(clauseOfOrder.SelectedItems); // Level 2 of Customer. cutomer = Assert.Single( clauseOfCustomer.SelectedItems.OfType <ExpandedNavigationSelectItem>().Where( item => item.PathToNavigationProperty.FirstSegment is NavigationPropertySegment && ((NavigationPropertySegment)item.PathToNavigationProperty.FirstSegment).NavigationProperty .Name == "Friend") ); Assert.Null(cutomer.LevelsOption); clauseOfCustomer = cutomer.SelectAndExpand; Assert.True(clauseOfCustomer.AllSelected); Assert.Empty(clauseOfCustomer.SelectedItems); }
/// <summary> /// Creates a new instance of <see cref="OrderByQueryOption"/> based on the raw $orderby value and /// an EdmModel from <see cref="ODataQueryContext"/>. /// </summary> /// <param name="rawValue">The raw value for $orderby query. It can be null or empty.</param> /// <param name="context">The <see cref="ODataQueryContext"/> which contains the <see cref="IEdmModel"/> and some type information</param> /// <param name="queryOptionParser">The <see cref="ODataQueryOptionParser"/> which is used to parse the query option.</param> /// <returns>A new <see cref="OrderByQueryOption"/> instance</returns> protected virtual OrderByQueryOption CreateOrderByQueryOption(string rawValue, ODataQueryContext context, ODataQueryOptionParser queryOptionParser) { return(new OrderByQueryOption(rawValue, context, queryOptionParser)); }
public void ProcessLevelsCorrectly_WithNestedLevels() { // Arrange var model = ODataLevelsTest.GetEdmModel(); var context = new ODataQueryContext( model, model.FindDeclaredType("Microsoft.AspNet.OData.Test.Routing.LevelsEntity")); context.RequestContainer = new MockContainer(); var selectExpand = new SelectExpandQueryOption( select: null, expand: "Parent($expand=DerivedAncestors($levels=2);$levels=max)", context: context); selectExpand.LevelsMaxLiteralExpansionDepth = 4; // Act SelectExpandClause clause = selectExpand.ProcessLevels(); // Assert Assert.True(clause.AllSelected); Assert.Single(clause.SelectedItems); // Level 1 of Parent. var parent = Assert.IsType <ExpandedNavigationSelectItem>(clause.SelectedItems.Single()); Assert.Equal( "Parent", ((NavigationPropertySegment)parent.PathToNavigationProperty.FirstSegment).NavigationProperty.Name); Assert.Null(parent.LevelsOption); var clauseOfParent = parent.SelectAndExpand; Assert.True(clauseOfParent.AllSelected); Assert.Equal(2, clauseOfParent.SelectedItems.Count()); // Level 1 of DerivedAncestors. var derivedAncestors = Assert.Single(clauseOfParent.SelectedItems.OfType <ExpandedNavigationSelectItem>().Where( item => item.PathToNavigationProperty.FirstSegment is NavigationPropertySegment && ((NavigationPropertySegment)item.PathToNavigationProperty.FirstSegment).NavigationProperty.Name == "DerivedAncestors")); Assert.Null(derivedAncestors.LevelsOption); var clauseOfDerivedAncestors = derivedAncestors.SelectAndExpand; Assert.True(clauseOfDerivedAncestors.AllSelected); Assert.Single(clauseOfDerivedAncestors.SelectedItems); // Level 2 of DerivedAncestors. derivedAncestors = Assert.IsType <ExpandedNavigationSelectItem>(clauseOfDerivedAncestors.SelectedItems.Single()); Assert.Equal( "DerivedAncestors", ((NavigationPropertySegment)derivedAncestors.PathToNavigationProperty.FirstSegment).NavigationProperty.Name); Assert.Null(derivedAncestors.LevelsOption); clauseOfDerivedAncestors = derivedAncestors.SelectAndExpand; Assert.True(clauseOfDerivedAncestors.AllSelected); Assert.Empty(clauseOfDerivedAncestors.SelectedItems); // Level 2 of Parent. parent = Assert.Single(clauseOfParent.SelectedItems.OfType <ExpandedNavigationSelectItem>().Where( item => item.PathToNavigationProperty.FirstSegment is NavigationPropertySegment && ((NavigationPropertySegment)item.PathToNavigationProperty.FirstSegment).NavigationProperty.Name == "Parent")); Assert.Null(parent.LevelsOption); clauseOfParent = parent.SelectAndExpand; Assert.True(clauseOfParent.AllSelected); Assert.Single(clauseOfParent.SelectedItems); // Level 1 of DerivedAncestors. derivedAncestors = Assert.Single(clauseOfParent.SelectedItems.OfType <ExpandedNavigationSelectItem>().Where( item => item.PathToNavigationProperty.FirstSegment is NavigationPropertySegment && ((NavigationPropertySegment)item.PathToNavigationProperty.FirstSegment).NavigationProperty.Name == "DerivedAncestors")); Assert.Null(derivedAncestors.LevelsOption); clauseOfDerivedAncestors = derivedAncestors.SelectAndExpand; Assert.True(clauseOfDerivedAncestors.AllSelected); Assert.Single(clauseOfDerivedAncestors.SelectedItems); // Level 2 of DerivedAncestors. derivedAncestors = Assert.IsType <ExpandedNavigationSelectItem>(clauseOfDerivedAncestors.SelectedItems.Single()); Assert.Equal( "DerivedAncestors", ((NavigationPropertySegment)derivedAncestors.PathToNavigationProperty.FirstSegment).NavigationProperty.Name); Assert.Null(derivedAncestors.LevelsOption); clauseOfDerivedAncestors = derivedAncestors.SelectAndExpand; Assert.True(clauseOfDerivedAncestors.AllSelected); Assert.Empty(clauseOfDerivedAncestors.SelectedItems); }
internal static ODataQueryValidator GetODataQueryValidator(ODataQueryContext context) { return(context?.RequestContainer?.GetRequiredService <ODataQueryValidator>() ?? new ODataQueryValidator()); }
public override async Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken) { if (actionExecutedContext?.Response?.Content is ObjectContent && actionExecutedContext?.Response?.IsSuccessStatusCode == true) { ObjectContent objContent = ((ObjectContent)(actionExecutedContext.Response.Content)); if (objContent.Value == null) { actionExecutedContext.Response.StatusCode = HttpStatusCode.NoContent; actionExecutedContext.Response.Content = null; } else { TypeInfo actionReturnType = objContent.Value.GetType().GetTypeInfo(); if (typeof(string) != actionReturnType && typeof(IEnumerable).GetTypeInfo().IsAssignableFrom(actionReturnType)) { bool isIQueryable = typeof(IQueryable).GetTypeInfo().IsAssignableFrom(actionReturnType); TypeInfo queryElementType = actionReturnType.HasElementType ? actionReturnType.GetElementType().GetTypeInfo() : actionReturnType.GetGenericArguments().First() /* Why not calling Single() ? http://stackoverflow.com/questions/41718323/why-variable-of-type-ienumerablesomething-gettype-getgenericargsuments-c */.GetTypeInfo(); IDataProviderSpecificMethodsProvider dataProviderSpecificMethodsProvider = null; if (isIQueryable == true) { IEnumerable <IDataProviderSpecificMethodsProvider> dataProviderSpecificMethodsProviders = actionExecutedContext.Request.GetOwinContext().GetDependencyResolver().ResolveAll <IDataProviderSpecificMethodsProvider>(); dataProviderSpecificMethodsProvider = (IDataProviderSpecificMethodsProvider)typeof(ODataEnableQueryAttribute).GetMethod(nameof(FindDataProviderSpecificMethodsProvider)).MakeGenericMethod(queryElementType).Invoke(this, new object[] { objContent.Value, dataProviderSpecificMethodsProviders }); } else { objContent.Value = typeof(ODataEnableQueryAttribute).GetMethod(nameof(ToQueryable)).MakeGenericMethod(queryElementType).Invoke(this, new object[] { objContent.Value }); } HttpRequestMessageProperties requestODataProps = actionExecutedContext.Request.ODataProperties(); ODataQueryContext currentOdataQueryContext = new ODataQueryContext(actionExecutedContext.Request.GetModel(), queryElementType, requestODataProps.Path); ODataQueryOptions currentOdataQueryOptions = new ODataQueryOptions(currentOdataQueryContext, actionExecutedContext.Request); ODataQuerySettings globalODataQuerySettings = new ODataQuerySettings { EnableConstantParameterization = this.EnableConstantParameterization, EnsureStableOrdering = this.EnsureStableOrdering, HandleNullPropagation = this.HandleNullPropagation, PageSize = this.DefaultPageSize }; ValidateQuery(actionExecutedContext.Request, currentOdataQueryOptions); int?currentQueryPageSize = currentOdataQueryOptions?.Top?.Value; int?globalQuerypageSize = globalODataQuerySettings.PageSize; int?takeCount = null; int?skipCount = currentOdataQueryOptions?.Skip?.Value; if (currentQueryPageSize.HasValue) { takeCount = currentQueryPageSize.Value; } else if (globalQuerypageSize.HasValue == true) { takeCount = globalQuerypageSize.Value; } else { takeCount = null; } globalODataQuerySettings.PageSize = null; // ApplyTo will enumerates the query for values other than null. We are gonna apply take in ToList & ToListAsync methods. if (currentOdataQueryOptions.Filter != null) { objContent.Value = currentOdataQueryOptions.Filter.ApplyTo(query: (IQueryable)objContent.Value, querySettings: globalODataQuerySettings); } if (currentOdataQueryOptions.Count?.Value == true && takeCount.HasValue == true) { long count = default(long); if (dataProviderSpecificMethodsProvider != null) { count = await(Task <long>) typeof(ODataEnableQueryAttribute).GetMethod(nameof(GetCountAsync)).MakeGenericMethod(queryElementType).Invoke(this, new object[] { objContent.Value, dataProviderSpecificMethodsProvider, cancellationToken }); } else { count = (long)typeof(ODataEnableQueryAttribute).GetMethod(nameof(GetCount)).MakeGenericMethod(queryElementType).Invoke(this, new object[] { objContent.Value }); } actionExecutedContext.Request.Properties["System.Web.OData.TotalCountFunc"] = new Func <long>(() => count); } objContent.Value = currentOdataQueryOptions.ApplyTo(query: (IQueryable)objContent.Value, querySettings: globalODataQuerySettings, ignoreQueryOptions: AllowedQueryOptions.Filter | AllowedQueryOptions.Skip | AllowedQueryOptions.Top); if (currentOdataQueryOptions.SelectExpand != null) { queryElementType = objContent.Value.GetType().GetTypeInfo().GetGenericArguments().Single().GetTypeInfo(); } if (dataProviderSpecificMethodsProvider != null) { objContent.Value = await(Task <object>) typeof(ODataEnableQueryAttribute).GetMethod(nameof(ToListAsync)).MakeGenericMethod(queryElementType).Invoke(this, new object[] { objContent.Value, dataProviderSpecificMethodsProvider, takeCount, skipCount, cancellationToken }); } else { objContent.Value = typeof(ODataEnableQueryAttribute).GetMethod(nameof(ToList)).MakeGenericMethod(queryElementType).Invoke(this, new object[] { objContent.Value, takeCount, skipCount }); } if (currentOdataQueryOptions.Count?.Value == true && takeCount.HasValue == false) { // We've no paging becuase there is no global config for max top and there is no top specified by the client's request, so the retured result of query's length is equivalent to total count of the query long count = ((IList)objContent.Value).Count; actionExecutedContext.Request.Properties["System.Web.OData.TotalCountFunc"] = new Func <long>(() => count); } } } } }