/// <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; ODataQueryOptions 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 IQueryable ApplyToImplementation(IQueryable query, SkipTokenQueryOption skipTokenQueryOption, ODataQuerySettings querySettings, ODataQueryOptions queryOptions) { if (query == null) { throw Error.ArgumentNull(nameof(query)); } if (skipTokenQueryOption == null) { throw Error.ArgumentNull(nameof(skipTokenQueryOption)); } if (querySettings == null) { throw Error.ArgumentNull(nameof(querySettings)); } ODataQueryContext context = skipTokenQueryOption.Context; if (context.ElementClrType == null) { throw Error.NotSupported(SRResources.ApplyToOnUntypedQueryOption, "ApplyTo"); } 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 void BuildQueryOptions(IDictionary <string, string> queryParameters) { foreach (var 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 = TryParseNonNegativeInteger("$top", kvp.Value); break; case "$skip": ThrowIfEmpty(kvp.Value, "$skip"); RawValues.Skip = kvp.Value; Skip = TryParseNonNegativeInteger("$skip", kvp.Value); break; case "$select": RawValues.Select = kvp.Value; Request.ODataFeature().SelectExpandClause = _queryOptionParser.ParseSelectAndExpand(); SelectExpand = new SelectExpandQueryOption(kvp.Value, string.Empty, Context, _queryOptionParser, Request); break; case "$count": // According to the OData 4 protocol, the value of this query option is optional: // http://docs.oasis-open.org/odata/odata/v4.0/errata02/os/complete/part1-protocol/odata-v4.0-errata02-os-part1-protocol-complete.html#_Toc406398308 // "A $count query option with a value of false (or not specified) hints that the service SHOULD NOT return a count." RawValues.Count = kvp.Value; if (string.IsNullOrWhiteSpace(kvp.Value) == false) { bool count; if (bool.TryParse(kvp.Value, out count)) { Count = count; } else { throw new ODataException($"If a value for the query '$count' is specified, it must have a value of '{bool.TrueString}' or '{bool.FalseString}'"); } } break; case "$expand": RawValues.Expand = kvp.Value; // TODO Parse the select statement if any Request.ODataFeature().SelectExpandClause = _queryOptionParser.ParseSelectAndExpand(); SelectExpand = new SelectExpandQueryOption(string.Empty, kvp.Value, Context, _queryOptionParser, Request); break; case "$format": RawValues.Format = kvp.Value; break; case "$skiptoken": RawValues.SkipToken = kvp.Value; break; } } }
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; SkipToken = new SkipTokenQueryOption(kvp.Value, Context); 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; case "$compute": ThrowIfEmpty(kvp.Value, "$compute"); RawValues.Compute = kvp.Value; Compute = new ComputeQueryOption(kvp.Value, Context, _queryOptionParser); break; case "$search": ThrowIfEmpty(kvp.Value, "$search"); RawValues.Search = kvp.Value; Search = new SearchQueryOption(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 (Request.IsCountRequest()) { Count = new CountQueryOption( "true", Context, new ODataQueryOptionParser( Context.Model, Context.ElementType, Context.NavigationSource, new Dictionary <string, string> { { "$count", "true" } }, Context.RequestContainer)); } }
/// <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(nameof(query)); } if (querySettings == null) { throw Error.ArgumentNull(nameof(querySettings)); } IQueryable result = query; IODataFeature odataFeature = Request.ODataFeature(); // Update the query setting querySettings = Context.UpdateQuerySettings(querySettings, 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); odataFeature.ApplyClause = Apply.ApplyClause; this.Context.ElementClrType = Apply.ResultClrType; } // TODO: need pass the result from $compute to the remaining query options // Construct the actual query and apply them in the following order: filter, orderby, skip, top if (IsAvailableODataQueryOption(Filter, AllowedQueryOptions.Filter)) { if (IsAvailableODataQueryOption(Compute, AllowedQueryOptions.Compute)) { Filter.Compute = Compute; } result = Filter.ApplyTo(result, querySettings); } // If both $search and $filter are specified in the same request, only those items satisfying both criteria are returned // apply $search if (IsAvailableODataQueryOption(Search, AllowedQueryOptions.Search)) { result = Search.ApplyTo(result, querySettings); } if (IsAvailableODataQueryOption(Count, AllowedQueryOptions.Count)) { if (odataFeature.TotalCountFunc == null) { Func <long> countFunc = Count.GetEntityCountFunc(result); if (countFunc != null) { odataFeature.TotalCountFunc = countFunc; } } if (Request.IsCountRequest()) { 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 = GenerateStableOrder(); } if (IsAvailableODataQueryOption(orderBy, AllowedQueryOptions.OrderBy)) { if (IsAvailableODataQueryOption(Compute, AllowedQueryOptions.Compute)) { orderBy.Compute = Compute; } result = orderBy.ApplyTo(result, querySettings); } if (IsAvailableODataQueryOption(SkipToken, AllowedQueryOptions.SkipToken)) { result = SkipToken.ApplyTo(result, querySettings, this); } AddAutoSelectExpandProperties(); if (SelectExpand != null) { var tempResult = ApplySelectExpand(result, querySettings); if (tempResult != default(IQueryable)) { result = tempResult; } } if (IsAvailableODataQueryOption(Skip, AllowedQueryOptions.Skip)) { result = Skip.ApplyTo(result, querySettings); } if (IsAvailableODataQueryOption(Top, AllowedQueryOptions.Top)) { result = Top.ApplyTo(result, querySettings); } result = ApplyPaging(result, querySettings); return(result); }
private void BuildQueryOptions(IDictionary <string, string> queryParameters) { foreach (var 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; } if (RawValues.Select != null || RawValues.Expand != null) { SelectExpand = new SelectExpandQueryOption(RawValues.Select, RawValues.Expand, Context); } } if (ODataCountMediaTypeMapping.IsCountRequest(Request)) { Count = new CountQueryOption( "true", Context, new ODataQueryOptionParser( Context.Model, Context.ElementType, Context.NavigationSource, new Dictionary <string, string> { { "$count", "true" } })); } }
/// <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> /// <param name="ignoreQueryOptions">The query parameters that are already applied in queries.</param> /// <returns>The new <see cref="IQueryable"/> after the query has been applied to.</returns> public virtual IQueryable ApplyTo(IQueryable query, ODataQuerySettings querySettings, AllowedQueryOptions ignoreQueryOptions) { _ignoreQueryOptions = ignoreQueryOptions; if (query == null) { throw Error.ArgumentNull("query"); } // Construct the actual query and apply them in the following order: filter if (IsAvailableODataQueryOption(Filter, AllowedQueryOptions.Filter)) { query = Filter.ApplyTo(query, querySettings, _assembliesResolver); } if (IsAvailableODataQueryOption(Count, AllowedQueryOptions.Count)) { if (Request.ODataProperties().TotalCountFunc == null) { Func <long> countFunc = Count.GetEntityCountFunc(query); if (countFunc != null) { Request.ODataProperties().TotalCountFunc = countFunc; } } if (ODataCountMediaTypeMapping.IsCountRequest(Request)) { return(query); } } 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)) { query = orderBy.ApplyTo(query, querySettings); } if (IsAvailableODataQueryOption(Skip, AllowedQueryOptions.Skip)) { query = Skip.ApplyTo(query, querySettings); } if (IsAvailableODataQueryOption(Top, AllowedQueryOptions.Top)) { query = Top.ApplyTo(query, querySettings); } AddAutoExpandProperties(querySettings); if (SelectExpand != null) { var tempResult = ApplySelectExpand(query, querySettings); if (tempResult != default(IQueryable)) { query = tempResult; } } if (querySettings.PageSize.HasValue) { bool resultsLimited = true; query = LimitResults(query, querySettings.PageSize.Value, out resultsLimited); var uriString = Request.GetDisplayUrl(); if (!string.IsNullOrWhiteSpace(uriString)) { var uri = new Uri(uriString); if (resultsLimited && uri != null && uri.IsAbsoluteUri && Request.ODataProperties().NextLink == null) { Uri nextPageLink = Request.GetNextPageLink(querySettings.PageSize.Value); Request.ODataProperties().NextLink = nextPageLink; } } } return(query); }