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)); }
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); }
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); }
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); }
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); }
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); }
/// <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)); }
/// <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()); }
/// <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); } }