public virtual void ValidateQuery(HttpRequest request, IODataQueryOptions queryOptions)
        {
            if (request == null)
            {
                throw Error.ArgumentNull("request");
            }

            if (queryOptions == null)
            {
                throw Error.ArgumentNull("queryOptions");
            }

            IEnumerable <KeyValuePair <string, StringValues> > queryParameters = request.Query;

            foreach (KeyValuePair <string, StringValues> kvp in queryParameters)
            {
                if (!queryOptions.IsSupportedQueryOption(kvp.Key) &&
                    kvp.Key.StartsWith("$", StringComparison.Ordinal))
                {
                    // we don't support any custom query options that start with $
                    // this should be caught be OnActionExecuted().
                    throw new ArgumentOutOfRangeException(kvp.Key);
                }
            }

            queryOptions.Validate(_validationSettings);
        }
        public virtual void ValidateQuery(HttpRequestMessage request, IODataQueryOptions queryOptions)
        {
            if (request == null)
            {
                throw Error.ArgumentNull("request");
            }

            if (queryOptions == null)
            {
                throw Error.ArgumentNull("queryOptions");
            }

            IEnumerable <KeyValuePair <string, string> > queryParameters = request.GetQueryNameValuePairs();

            foreach (KeyValuePair <string, string> kvp in queryParameters)
            {
                if (!queryOptions.IsSupportedQueryOption(kvp.Key) &&
                    kvp.Key.StartsWith("$", StringComparison.Ordinal))
                {
                    // we don't support any custom query options that start with $
                    throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.BadRequest,
                                                                                Error.Format(SRResources.QueryParameterNotSupported, kvp.Key)));
                }
            }

            queryOptions.Validate(_validationSettings);
        }
 /// <summary>
 /// Apply OData query options except $select and $expand parameters.
 /// </summary>
 /// <typeparam name="T">The type param.</typeparam>
 /// <param name="query">The OData aware query.</param>
 /// <param name="rawQueryOptions">The query options.</param>
 /// <param name="entitySetName">The entity set name.</param>
 /// <returns>The query <see cref="IQueryable{T}"/> with applied OData parameters.</returns>
 public static IQueryable <T> ApplyQueryOptionsWithoutSelectExpand <T>(
     this ODataQuery <T> query,
     IODataQueryOptions rawQueryOptions,
     string entitySetName = null)
 {
     return(ApplyQueryOptionsInternal(query, rawQueryOptions, entitySetName));
 }
 /// <summary>
 /// Validate the select and expand options.
 /// </summary>
 /// <param name="queryOptions">The query options.</param>
 internal static void ValidateSelectExpandOnly(IODataQueryOptions queryOptions)
 {
     if (queryOptions.Filter != null || queryOptions.Count != null || queryOptions.OrderBy != null ||
         queryOptions.Skip != null || queryOptions.Top != null)
     {
         throw new ODataException(Error.Format(SRResources.NonSelectExpandOnSingleEntity));
     }
 }
 /// <summary>
 /// Apply OData query options. Warning! not all providers support it.
 /// </summary>
 /// <typeparam name="T">The type param.</typeparam>
 /// <param name="query">The OData aware query.</param>
 /// <param name="rawQueryOptions">The query options.</param>
 /// <param name="entitySetName">The entity set name.</param>
 /// <returns>The query with special type of results <see cref="IQueryable{ISelectExpandWrapper}"/>.</returns>
 public static IQueryable <ISelectExpandWrapper> ApplyQueryOptionsAsQueryable <T>(
     this ODataQuery <T> query,
     IODataQueryOptions rawQueryOptions,
     string entitySetName = null)
 {
     return(ApplyQueryOptionsInternal(query, rawQueryOptions, entitySetName).SelectExpandAsQueryable(
                rawQueryOptions.Select,
                rawQueryOptions.Expand,
                entitySetName));
 }
        /// <summary>
        /// Execute the query.
        /// </summary>
        /// <param name="responseValue">The response value.</param>
        /// <param name="singleResultCollection">The content as SingleResult.Queryable.</param>
        /// <param name="actionDescriptor">The action context, i.e. action and controller name.</param>
        /// <param name="modelFunction">A function to get the model.</param>
        /// <param name="request">The internal request.</param>
        /// <param name="createQueryOptionFunction">A function used to create and validate query options.</param>
        /// <returns></returns>
        private object ExecuteQuery(
            object responseValue,
            IQueryable singleResultCollection,
            IWebApiActionDescriptor actionDescriptor,
            Func <Type, IEdmModel> modelFunction,
            IWebApiRequestMessage request,
            Func <ODataQueryContext, IODataQueryOptions> createQueryOptionFunction)
        {
            ODataQueryContext queryContext = GetODataQueryContext(responseValue, singleResultCollection, actionDescriptor, modelFunction, request.Context.Path);

            // Create and validate the query options.
            IODataQueryOptions queryOptions = createQueryOptionFunction(queryContext);

            // apply the query
            IEnumerable enumerable = responseValue as IEnumerable;

            if (enumerable == null || responseValue is string || responseValue is byte[])
            {
                // response is not a collection; we only support $select and $expand on single entities.
                ValidateSelectExpandOnly(queryOptions);

                if (singleResultCollection == null)
                {
                    // response is a single entity.
                    return(ApplyQuery(entity: responseValue, queryOptions: queryOptions));
                }
                else
                {
                    IQueryable queryable = singleResultCollection as IQueryable;
                    queryable = ApplyQuery(queryable, queryOptions);
                    return(SingleOrDefault(queryable, actionDescriptor));
                }
            }
            else
            {
                // response is a collection.
                IQueryable queryable = (enumerable as IQueryable) ?? enumerable.AsQueryable();
                queryable = ApplyQuery(queryable, queryOptions);

                if (request.IsCountRequest())
                {
                    long?count = request.Context.TotalCount;

                    if (count.HasValue)
                    {
                        // Return the count value if it is a $count request.
                        return(count.Value);
                    }
                }

                return(queryable);
            }
        }
        /// <summary>
        /// Applies the query to the given entity based on incoming query from uri and query settings.
        /// </summary>
        /// <param name="entity">The original entity from the response message.</param>
        /// <param name="queryOptions">
        /// The <see cref="IODataQueryOptions"/> instance constructed based on the incoming request.
        /// </param>
        /// <returns>The new entity after the $select and $expand query has been applied to.</returns>
        public virtual object ApplyQuery(object entity, IODataQueryOptions queryOptions)
        {
            if (entity == null)
            {
                throw Error.ArgumentNull("entity");
            }
            if (queryOptions == null)
            {
                throw Error.ArgumentNull("queryOptions");
            }

            return(queryOptions.ApplyTo(entity, _querySettings));
        }
        /// <summary>
        /// Applies the query to the given IQueryable based on incoming query from uri and query settings. By default,
        /// the implementation supports $top, $skip, $orderby and $filter. Override this method to perform additional
        /// query composition of the query.
        /// </summary>
        /// <param name="queryable">The original queryable instance from the response message.</param>
        /// <param name="queryOptions">
        /// The <see cref="IODataQueryOptions"/> instance constructed based on the incoming request.
        /// </param>
        public virtual IQueryable ApplyQuery(IQueryable queryable, IODataQueryOptions queryOptions)
        {
            if (queryable == null)
            {
                throw Error.ArgumentNull("queryable");
            }
            if (queryOptions == null)
            {
                throw Error.ArgumentNull("queryOptions");
            }

            return(queryOptions.ApplyTo(queryable, _querySettings));
        }
Example #9
0
        public void OrderBy_Works_QueryOptionCaseInsensitive()
        {
            // Arrange
            const string orderBy = "$oRdeRby=naMe";

            // Act
            IODataQueryOptions queryOptions = GetQueryOptions(orderBy);

            // Assert
            Assert.NotNull(queryOptions.OrderBy);
            OrderByClause orderByClause        = queryOptions.OrderBy.OrderByClause;
            SingleValuePropertyAccessNode node = Assert.IsType <SingleValuePropertyAccessNode>(orderByClause.Expression);

            Assert.Equal("Name", node.Property.Name);
        }
Example #10
0
        public void Filter_Works_QueryOptionCaseInsensitive()
        {
            // Arrange
            const string filter = "$FiLtEr=name eQ 'nba'";

            // Act
            IODataQueryOptions queryOptions = GetQueryOptions(filter);

            // Assert
            Assert.NotNull(queryOptions.Filter);
            FilterClause       filterClause = queryOptions.Filter.FilterClause;
            BinaryOperatorNode node         = Assert.IsType <BinaryOperatorNode>(filterClause.Expression);

            Assert.Equal(BinaryOperatorKind.Equal, node.OperatorKind);
        }
Example #11
0
        public void Expand_Works_QueryOptionCaseInsensitive()
        {
            // Arrange
            const string expand = "$ExPAnd=ProdUCts";

            // Act
            IODataQueryOptions queryOptions = GetQueryOptions(expand);

            // Assert
            Assert.NotNull(queryOptions.SelectExpand);
            SelectExpandClause           expandClause = queryOptions.SelectExpand.SelectExpandClause;
            ExpandedNavigationSelectItem expandItem   = Assert.IsType <ExpandedNavigationSelectItem>(
                Assert.Single(expandClause.SelectedItems));
            NavigationPropertySegment segment =
                Assert.IsType <NavigationPropertySegment>(expandItem.PathToNavigationProperty.FirstSegment);

            Assert.Equal("Products", segment.NavigationProperty.Name);
        }
Example #12
0
        public void Select_Works_QueryOptionCaseInsensitive()
        {
            // Arrange
            const string select = "$SeLecT=naMe";

            // Act
            IODataQueryOptions queryOptions = GetQueryOptions(select);

            // Assert
            Assert.NotNull(queryOptions.SelectExpand);
            SelectExpandClause selectClause   = queryOptions.SelectExpand.SelectExpandClause;
            SelectItem         selectItem     = Assert.Single(selectClause.SelectedItems);
            PathSelectItem     pathSelectItem = Assert.IsType <PathSelectItem>(selectItem);

            Assert.NotNull(pathSelectItem.SelectedPath);
            PropertySegment segment = Assert.IsType <PropertySegment>(pathSelectItem.SelectedPath.FirstSegment);

            Assert.Equal("Name", segment.Property.Name);
        }
Example #13
0
        public ITestActionResult Get(IODataQueryOptions <CollectionSerializerCustomer> options)
        {
            IQueryable <CollectionSerializerCustomer> customers = new[]
            {
                new CollectionSerializerCustomer {
                    ID = 1, Name = "Name 1"
                },
                new CollectionSerializerCustomer {
                    ID = 2, Name = "Name 2"
                },
                new CollectionSerializerCustomer {
                    ID = 3, Name = "Name 3"
                },
            }.AsQueryable();

            IQueryable <IEdmEntityObject> appliedCustomers = options.ApplyTo(customers) as IQueryable <IEdmEntityObject>;

            return(Ok(appliedCustomers));
        }
        /// <summary>
        /// Apply the $skiptoken query to the given IQueryable.
        /// </summary>
        /// <param name="query">The original <see cref="IQueryable"/>.</param>
        /// <param name="skipTokenQueryOption">The skiptoken query option which needs to be applied to this query option.</param>
        /// <returns>The new <see cref="IQueryable"/> after the skiptoken query has been applied to.</returns>
        public override IQueryable ApplyTo(IQueryable query, SkipTokenQueryOption skipTokenQueryOption)
        {
            if (skipTokenQueryOption == null)
            {
                throw Error.ArgumentNullOrEmpty("skipTokenQueryOption");
            }

            ODataQuerySettings  querySettings = skipTokenQueryOption.QuerySettings;
            IODataQueryOptions  queryOptions  = skipTokenQueryOption.QueryOptions;
            IList <OrderByNode> orderByNodes  = null;

            if (queryOptions != null)
            {
                OrderByQueryOption orderBy = queryOptions.GenerateStableOrder();
                if (orderBy != null)
                {
                    orderByNodes = orderBy.OrderByNodes;
                }
            }

            return(ApplyToCore(query, querySettings, orderByNodes, skipTokenQueryOption.Context, skipTokenQueryOption.RawValue));
        }
        private static ODataQuery <T> ApplyQueryOptionsInternal <T>(ODataQuery <T> query, IODataQueryOptions rawQueryOptions, string entitySetName)
        {
            if (query == null)
            {
                throw new ArgumentNullException(nameof(query));
            }
            if (rawQueryOptions == null)
            {
                throw new ArgumentNullException(nameof(rawQueryOptions));
            }

            if (rawQueryOptions.Filters != null)
            {
                foreach (string filter in rawQueryOptions.Filters)
                {
                    query = query.Filter(filter, entitySetName);
                }
            }

            if (rawQueryOptions.OrderBy != null)
            {
                query = query.OrderBy(rawQueryOptions.OrderBy, entitySetName);
            }

            query = query.TopSkip(rawQueryOptions.Top, rawQueryOptions.Skip);

            return(query);
        }
Example #16
0
 /// <summary>
 /// Apply the $skiptoken query to the given IQueryable.
 /// </summary>
 /// <param name="query">The original <see cref="IQueryable"/>.</param>
 /// <param name="querySettings">The query settings to use while applying this query option.</param>
 /// <param name="queryOptions">Information about the other query options.</param>
 /// <returns>The new <see cref="IQueryable"/> after the skiptoken query has been applied to.</returns>
 public virtual IQueryable ApplyTo(IQueryable query, ODataQuerySettings querySettings, IODataQueryOptions queryOptions)
 {
     QuerySettings = querySettings;
     QueryOptions  = queryOptions;
     return(skipTokenHandler.ApplyTo(query, this));
 }
Example #17
0
 /// <summary>
 /// Apply the $skiptoken query to the given IQueryable.
 /// </summary>
 /// <param name="query">The original <see cref="IQueryable"/>.</param>
 /// <param name="querySettings">The query settings to use while applying this query option.</param>
 /// <param name="queryOptions">Information about the other query options.</param>
 /// <returns>The new <see cref="IQueryable"/> after the skiptoken query has been applied to.</returns>
 public virtual IQueryable <T> ApplyTo <T>(IQueryable <T> query, ODataQuerySettings querySettings, IODataQueryOptions queryOptions)
 {
     QuerySettings = querySettings;
     QueryOptions  = queryOptions;
     return(skipTokenHandler.ApplyTo <T>(query, this) as IOrderedQueryable <T>);
 }
            public Task BindModelAsync(ModelBindingContext bindingContext)
            {
                if (bindingContext == null)
                {
                    throw Error.ArgumentNull("bindingContext");
                }

                HttpRequest request = bindingContext.HttpContext.Request;

                if (request == null)
                {
                    throw Error.Argument("actionContext", SRResources.ActionContextMustHaveRequest);
                }

                ActionDescriptor actionDescriptor = bindingContext.ActionContext.ActionDescriptor;

                if (actionDescriptor == null)
                {
                    throw Error.Argument("actionContext", SRResources.ActionContextMustHaveDescriptor);
                }


                // Get the parameter description of the parameter to bind.
                ParameterDescriptor paramDescriptor = bindingContext.ActionContext.ActionDescriptor.Parameters
                                                      .Where(p => p.Name == bindingContext.FieldName)
                                                      .FirstOrDefault();

                // Now make sure parameter type is ODataQueryOptions or ODataQueryOptions<T>.
                Type parameterType = paramDescriptor?.ParameterType;

                if (IsODataQueryOptions(parameterType))
                {
                    // Get the entity type from the parameter type if it is ODataQueryOptions<T>.
                    // Fall back to the return type if not. Also, note that the entity type from the return type and ODataQueryOptions<T>
                    // can be different (example implementing $select or $expand).
                    Type entityClrType = null;
                    if (paramDescriptor != null)
                    {
                        entityClrType = GetEntityClrTypeFromParameterType(parameterType);
                    }

                    if (entityClrType == null)
                    {
                        entityClrType = GetEntityClrTypeFromActionReturnType(actionDescriptor);
                    }

                    IEdmModel         userModel        = request.GetModel();
                    IEdmModel         model            = userModel != EdmCoreModel.Instance ? userModel : actionDescriptor.GetEdmModel(request, entityClrType);
                    ODataQueryContext entitySetContext = new ODataQueryContext(model, entityClrType, request.ODataFeature().Path);

                    Func <ODataQueryContext, HttpRequest, IODataQueryOptions> createODataQueryOptions;
                    object constructorAsObject = null;
                    if (actionDescriptor.Properties.TryGetValue(CreateODataQueryOptionsCtorKey, out constructorAsObject))
                    {
                        createODataQueryOptions = (Func <ODataQueryContext, HttpRequest, IODataQueryOptions>)constructorAsObject;
                    }
                    else
                    {
                        createODataQueryOptions = (Func <ODataQueryContext, HttpRequest, IODataQueryOptions>)
                                                  Delegate.CreateDelegate(typeof(Func <ODataQueryContext, HttpRequest, IODataQueryOptions>),
                                                                          _createODataQueryOptions.MakeGenericMethod(entityClrType));
                    };

                    IODataQueryOptions parameterValue = createODataQueryOptions(entitySetContext, request);
                    bindingContext.Result = ModelBindingResult.Success(parameterValue);
                }

                return(TaskHelpers.Completed());
            }
Example #19
0
        /// <summary>
        /// Validates the OData query.
        /// </summary>
        /// <param name="options">The OData query to validate.</param>
        /// <param name="validationSettings">The validation settings.</param>
        public virtual void Validate(IODataQueryOptions options, ODataValidationSettings validationSettings)
        {
            if (options == null)
            {
                throw Error.ArgumentNull("options");
            }

            if (validationSettings == null)
            {
                throw Error.ArgumentNull("validationSettings");
            }

            // Validate each query options
            if (options.Apply != null)
            {
                if (options.Apply.ApplyClause != null)
                {
                    ValidateQueryOptionAllowed(AllowedQueryOptions.Apply, validationSettings.AllowedQueryOptions);
                }
            }

            if (options.Skip != null)
            {
                ValidateQueryOptionAllowed(AllowedQueryOptions.Skip, validationSettings.AllowedQueryOptions);
                options.Skip.Validate(validationSettings);
            }

            if (options.Top != null)
            {
                ValidateQueryOptionAllowed(AllowedQueryOptions.Top, validationSettings.AllowedQueryOptions);
                options.Top.Validate(validationSettings);
            }

            if (options.OrderBy != null)
            {
                ValidateQueryOptionAllowed(AllowedQueryOptions.OrderBy, validationSettings.AllowedQueryOptions);
                options.OrderBy.Validate(validationSettings);
            }

            if (options.Filter != null)
            {
                ValidateQueryOptionAllowed(AllowedQueryOptions.Filter, validationSettings.AllowedQueryOptions);
                options.Filter.Validate(validationSettings);
            }

            if (options is ODataQueryOptions _options)
            {
                if (_options.Count != null || _options.InternalRequest.IsCountRequest())
                {
                    ValidateQueryOptionAllowed(AllowedQueryOptions.Count, validationSettings.AllowedQueryOptions);

                    if (_options.Count != null)
                    {
                        _options.Count.Validate(validationSettings);
                    }
                }
            }

            if (options.SkipToken != null)
            {
                ValidateQueryOptionAllowed(AllowedQueryOptions.SkipToken, validationSettings.AllowedQueryOptions);
                options.SkipToken.Validate(validationSettings);
            }

            if (options.RawValues.Expand != null)
            {
                ValidateQueryOptionAllowed(AllowedQueryOptions.Expand, validationSettings.AllowedQueryOptions);
            }

            if (options.RawValues.Select != null)
            {
                ValidateQueryOptionAllowed(AllowedQueryOptions.Select, validationSettings.AllowedQueryOptions);
            }

            if (options.SelectExpand != null)
            {
                options.SelectExpand.Validate(validationSettings);
            }

            if (options.RawValues.Format != null)
            {
                ValidateQueryOptionAllowed(AllowedQueryOptions.Format, validationSettings.AllowedQueryOptions);
            }

            if (options.RawValues.SkipToken != null)
            {
                ValidateQueryOptionAllowed(AllowedQueryOptions.SkipToken, validationSettings.AllowedQueryOptions);
            }

            if (options.RawValues.DeltaToken != null)
            {
                ValidateQueryOptionAllowed(AllowedQueryOptions.DeltaToken, validationSettings.AllowedQueryOptions);
            }
        }