/// <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));
        }
Exemple #2
0
        private void BuildQueryOptions(IDictionary <string, string> queryParameters)
        {
            foreach (KeyValuePair <string, string> kvp in queryParameters)
            {
                switch (kvp.Key.ToLowerInvariant())
                {
                case "$filter":
                    ThrowIfEmpty(kvp.Value, "$filter");
                    RawValues.Filter = kvp.Value;
                    Filter           = new FilterQueryOption(kvp.Value, Context, _queryOptionParser);
                    break;

                case "$orderby":
                    ThrowIfEmpty(kvp.Value, "$orderby");
                    RawValues.OrderBy = kvp.Value;
                    OrderBy           = new OrderByQueryOption(kvp.Value, Context, _queryOptionParser);
                    break;

                case "$top":
                    ThrowIfEmpty(kvp.Value, "$top");
                    RawValues.Top = kvp.Value;
                    Top           = new TopQueryOption(kvp.Value, Context, _queryOptionParser);
                    break;

                case "$skip":
                    ThrowIfEmpty(kvp.Value, "$skip");
                    RawValues.Skip = kvp.Value;
                    Skip           = new SkipQueryOption(kvp.Value, Context, _queryOptionParser);
                    break;

                case "$select":
                    RawValues.Select = kvp.Value;
                    break;

                case "$count":
                    ThrowIfEmpty(kvp.Value, "$count");
                    RawValues.Count = kvp.Value;
                    Count           = new CountQueryOption(kvp.Value, Context, _queryOptionParser);
                    break;

                case "$expand":
                    RawValues.Expand = kvp.Value;
                    break;

                case "$format":
                    RawValues.Format = kvp.Value;
                    break;

                case "$skiptoken":
                    RawValues.SkipToken = kvp.Value;
                    SkipToken           = new SkipTokenQueryOption(kvp.Value, Context, _queryOptionParser);
                    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;

                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 (InternalRequest.IsCountRequest())
            {
                Count = new CountQueryOption(
                    "true",
                    Context,
                    new ODataQueryOptionParser(
                        Context.Model,
                        Context.ElementType,
                        Context.NavigationSource,
                        new Dictionary <string, string> {
                    { "$count", "true" }
                },
                        Context.RequestContainer));
            }
        }
Exemple #3
0
        public virtual IQueryable ApplyTo(IQueryable query, ODataQuerySettings querySettings)
        {
            if (query == null)
            {
                throw Error.ArgumentNull("query");
            }

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

            IQueryable result = query;

            // First apply $apply
            // Section 3.15 of the spec http://docs.oasis-open.org/odata/odata-data-aggregation-ext/v4.0/cs01/odata-data-aggregation-ext-v4.0-cs01.html#_Toc378326311
            if (IsAvailableODataQueryOption(Apply, AllowedQueryOptions.Apply))
            {
                result = Apply.ApplyTo(result, querySettings);
                InternalRequest.Context.ApplyClause = Apply.ApplyClause;
                if (Apply.SelectExpandClause != null)
                {
                    // In case of just expand in $apply falling back to $expand serialization
                    InternalRequest.Context.ProcessedSelectExpandClause = Apply.SelectExpandClause;
                }
                this.Context.ElementClrType = Apply.ResultClrType;
            }

            // Apply compute
            // It should be executed before $filter, because it defines computed properties that can be used in a $select or within a $filter or $orderby expression.
            if (IsAvailableODataQueryOption(Compute, AllowedQueryOptions.Compute))
            {
                result = Compute.ApplyTo(result, querySettings);
            }

            // Construct the actual query and apply them in the following order: filter, orderby, skip, top
            if (IsAvailableODataQueryOption(Filter, AllowedQueryOptions.Filter))
            {
                result = Filter.ApplyTo(result, querySettings);
            }

            if (IsAvailableODataQueryOption(Count, AllowedQueryOptions.Count))
            {
                if (InternalRequest.Context.TotalCountFunc == null)
                {
                    Func <long> countFunc = Count.GetEntityCountFunc(result);
                    if (countFunc != null)
                    {
                        InternalRequest.Context.TotalCountFunc = countFunc;
                    }
                }

                if (InternalRequest.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))
            {
                result = orderBy.ApplyTo(result, querySettings);
            }

            if (IsAvailableODataQueryOption(SkipToken, AllowedQueryOptions.SkipToken))
            {
                result = SkipToken.ApplyTo(result, querySettings, this);
            }


            if (!IsAggregated(Apply?.ApplyClause))
            {
                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);
        }
Exemple #4
0
        public virtual IQueryable ApplyTo(IQueryable query, ODataQuerySettings querySettings)
        {
            if (query == null)
            {
                throw Error.ArgumentNull("query");
            }

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

            IQueryable result = query;

            // First apply $apply
            // Section 3.15 of the spec http://docs.oasis-open.org/odata/odata-data-aggregation-ext/v4.0/cs01/odata-data-aggregation-ext-v4.0-cs01.html#_Toc378326311
            ApplyClause apply = null;

            if (IsAvailableODataQueryOption(Apply, AllowedQueryOptions.Apply))
            {
                result = Apply.ApplyTo(result, querySettings);
                InternalRequest.Context.ApplyClause = Apply.ApplyClause;
                this.Context.ElementClrType         = Apply.ResultClrType;
                apply = Apply.ApplyClause;
            }

            // Construct the actual query and apply them in the following order: filter, orderby, skip, top
            if (IsAvailableODataQueryOption(Filter, AllowedQueryOptions.Filter))
            {
                result = Filter.ApplyTo(result, querySettings);
            }

            if (IsAvailableODataQueryOption(Count, AllowedQueryOptions.Count))
            {
                if (InternalRequest.Context.TotalCountFunc == null)
                {
                    Func <long> countFunc = Count.GetEntityCountFunc(result);
                    if (countFunc != null)
                    {
                        InternalRequest.Context.TotalCountFunc = countFunc;
                    }
                }

                if (InternalRequest.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).
                List <string> applySortOptions = GetApplySortOptions(apply);

                orderBy = orderBy == null
                            ? GenerateDefaultOrderBy(Context, applySortOptions)
                            : EnsureStableSortOrderBy(orderBy, Context, applySortOptions);
            }

            if (IsAvailableODataQueryOption(orderBy, AllowedQueryOptions.OrderBy))
            {
                result = orderBy.ApplyTo(result, querySettings);
            }

            if (IsAvailableODataQueryOption(Skip, AllowedQueryOptions.Skip))
            {
                result = Skip.ApplyTo(result, querySettings);
            }

            if (IsAvailableODataQueryOption(Top, AllowedQueryOptions.Top))
            {
                result = Top.ApplyTo(result, querySettings);
            }

            AddAutoSelectExpandProperties();

            if (SelectExpand != null)
            {
                var tempResult = ApplySelectExpand(result, querySettings);
                if (tempResult != default(IQueryable))
                {
                    result = tempResult;
                }
            }

            int pageSize = -1;

            if (querySettings.PageSize.HasValue)
            {
                pageSize = querySettings.PageSize.Value;
            }
            else if (querySettings.ModelBoundPageSize.HasValue)
            {
                pageSize = querySettings.ModelBoundPageSize.Value;
            }

            if (pageSize > 0)
            {
                bool resultsLimited;
                result = LimitResults(result, pageSize, out resultsLimited);
                if (resultsLimited && InternalRequest.RequestUri != null && InternalRequest.RequestUri.IsAbsoluteUri &&
                    InternalRequest.Context.NextLink == null)
                {
                    Uri nextPageLink = InternalRequest.GetNextPageLink(pageSize);
                    InternalRequest.Context.NextLink = nextPageLink;
                }
            }

            return(result);
        }