コード例 #1
0
        /// <inheritdoc />
        public virtual ODataSerializer GetODataPayloadSerializer(HttpContext context, Type type)
        {
            if (context == null)
            {
                throw Error.ArgumentNull("context");
            }

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

            IServiceProvider provider = context.RequestServices;
            var x = context.Response.StatusCode;

            // handle the special types.
            if (type == typeof(ODataServiceDocument))
            {
                return(provider.GetRequiredService <ODataServiceDocumentSerializer>());
            }
            else if (type == typeof(Uri) || type == typeof(ODataEntityReferenceLink))
            {
                return(provider.GetRequiredService <ODataEntityReferenceLinkSerializer>());
            }
            else if (typeof(IEnumerable <Uri>).IsAssignableFrom(type) || type == typeof(ODataEntityReferenceLinks))
            {
                return(provider.GetRequiredService <ODataEntityReferenceLinksSerializer>());
            }
            else if (type == typeof(ODataError) || type == typeof(SerializableError))
            {
                return(provider.GetRequiredService <ODataErrorSerializer>());
            }
            else if (typeof(IEdmModel).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo()))
            {
                return(provider.GetRequiredService <ODataMetadataSerializer>());
            }

            // if it is not a special type, assume it has a corresponding EdmType.
            IEdmModel         model            = context.ODataFeature().Model;
            ClrTypeCache      typeMappingCache = model.GetTypeMappingCache();
            IEdmTypeReference edmType          = typeMappingCache.GetEdmType(type, model);

            if (edmType != null)
            {
                if (((edmType.IsPrimitive() || edmType.IsEnum()) &&
                     ODataRawValueMediaTypeMapping.IsRawValueRequest(context)) ||
                    ODataCountMediaTypeMapping.IsCountRequest(context))
                {
                    return(provider.GetRequiredService <ODataRawValueSerializer>());
                }
                else
                {
                    return(GetEdmTypeSerializer(context, edmType));
                }
            }
            else
            {
                return(null);
            }
        }
コード例 #2
0
        public override void OnActionExecuted(ActionExecutedContext context)
        {
            if (context == null)
            {
                throw Error.ArgumentNull("context");
            }

            var response = context.HttpContext.Response;

            if (!response.IsSuccessStatusCode())
            {
                return;
            }

            var request = context.HttpContext.Request;

            if (request.HasQueryOptions() ||
                ODataCountMediaTypeMapping.IsCountRequest(request) ||
                context.ActionDescriptor.HasQueryOption())
            {
                var result = context.Result as ObjectResult;
                if (result == null)
                {
                    throw Error.Argument("context", SRResources.QueryingRequiresObjectContent, context.Result.GetType().FullName);
                }

                if (result.Value != null)
                {
                    result.Value = ApplyQueryOptions(result.Value, request, context.ActionDescriptor, context.HttpContext.RequestServices.GetService <AssembliesResolver>());
                }
            }
        }
コード例 #3
0
        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);
        }
コード例 #4
0
        /// <inheritdoc />
        public override ODataSerializer GetODataPayloadSerializer(IEdmModel model, Type type, HttpRequestMessage request)
        {
            if (model == null)
            {
                throw Error.ArgumentNull("model");
            }
            if (type == null)
            {
                throw Error.ArgumentNull("type");
            }
            if (request == null)
            {
                throw Error.ArgumentNull("request");
            }

            // handle the special types.
            if (type == typeof(ODataServiceDocument))
            {
                return(_workspaceSerializer);
            }
            else if (type == typeof(Uri) || type == typeof(ODataEntityReferenceLink))
            {
                return(_entityReferenceLinkSerializer);
            }
            else if (typeof(IEnumerable <Uri>).IsAssignableFrom(type) || type == typeof(ODataEntityReferenceLinks))
            {
                return(_entityReferenceLinksSerializer);
            }
            else if (type == typeof(ODataError) || type == typeof(HttpError))
            {
                return(_errorSerializer);
            }
            else if (typeof(IEdmModel).IsAssignableFrom(type))
            {
                return(_metadataSerializer);
            }

            // if it is not a special type, assume it has a corresponding EdmType.
            ClrTypeCache      typeMappingCache = model.GetTypeMappingCache();
            IEdmTypeReference edmType          = typeMappingCache.GetEdmType(type, model);

            if (edmType != null)
            {
                if (((edmType.IsPrimitive() || edmType.IsEnum()) &&
                     ODataRawValueMediaTypeMapping.IsRawValueRequest(request)) ||
                    ODataCountMediaTypeMapping.IsCountRequest(request))
                {
                    return(_rawValueSerializer);
                }
                else
                {
                    return(GetEdmTypeSerializer(edmType));
                }
            }
            else
            {
                return(null);
            }
        }
コード例 #5
0
        /// <summary>
        /// Returns true if the query should be applied
        /// </summary>
        /// <param name="responseContent">The response content</param>
        /// <param name="request">The incoming request</param>
        /// <param name="actionDescriptor">>The action descriptor for the action being queried on.</param>
        /// <returns></returns>
        protected virtual bool ShouldApplyQuery(ObjectContent responseContent, HttpRequestMessage request, HttpActionDescriptor actionDescriptor)
        {
            bool shouldApplyQuery = responseContent.Value != null &&
                                    request.RequestUri != null &&
                                    (!String.IsNullOrWhiteSpace(request.RequestUri.Query) ||
                                     _querySettings.PageSize.HasValue ||
                                     responseContent.Value is SingleResult ||
                                     ODataCountMediaTypeMapping.IsCountRequest(request) ||
                                     ContainsAutoExpandProperty(responseContent.Value, request, actionDescriptor));

            return(shouldApplyQuery);
        }
コード例 #6
0
        private object ExecuteQuery(object response, HttpRequestMessage request, HttpActionDescriptor actionDescriptor, ODataQueryContext queryContext)
        {
            if (queryContext == null)
            {
                queryContext = GetODataQueryContext(response, request, actionDescriptor);
            }

            ODataQueryOptions queryOptions = new ODataQueryOptions(queryContext, request);

            ValidateQuery(request, queryOptions);

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

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

                SingleResult singleResult = response as SingleResult;
                if (singleResult == null)
                {
                    // response is a single entity.
                    return(ApplyQuery(entity: response, queryOptions: queryOptions));
                }
                else
                {
                    // response is a composable SingleResult. ApplyQuery and call SingleOrDefault.
                    IQueryable queryable = singleResult.Queryable;
                    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 (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(queryable);
            }
        }
コード例 #7
0
        public override void OnActionExecuted(ActionExecutedContext context)
        {
            if (context == null)
            {
                throw Error.ArgumentNull("context");
            }

            var response = context.HttpContext.Response;

            if (!response.IsSuccessStatusCode())
            {
                return;
            }

            var request = context.HttpContext.Request;

            var result = context.Result as ObjectResult;

            if (request.HasQueryOptions() ||
                ODataCountMediaTypeMapping.IsCountRequest(request) ||
                context.ActionDescriptor.HasQueryOption())
            {
                if (result == null)
                {
                    throw Error.Argument("context", SRResources.QueryingRequiresObjectContent, context.Result.GetType().FullName);
                }

                if (result.Value != null)
                {
                    result.Value = ApplyQueryOptions(result.Value, request, context.ActionDescriptor, context.HttpContext.RequestServices.GetService <AssembliesResolver>());
                }
            }
            if (result != null && ShouldBeSingleEntity(request.ODataProperties().Path.PathTemplate))
            {
                var queryable = result.Value as IQueryable;
                if (queryable != null)
                {
                    result.Value = SingleOrDefault(queryable, context.ActionDescriptor);
                }
            }
        }
コード例 #8
0
        protected virtual async Task <object> ApplyQueryOptionsAsync(object value,
                                                                     ODataQueryOptions options)
        {
            var enumerable = value as IEnumerable;

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

                //options.Request.ODataFeature().IsEnumerated = true;
                var singleResult = value as SingleResult;
                if (singleResult == null)
                {
                    // response is a single entity.
                    return(await ApplyQueryObjectAsync(value, options, false));
                }
                // response is a composable SingleResult. ApplyQuery and call SingleOrDefault.
                var singleQueryable = singleResult.Queryable;
                singleQueryable = await ApplyQueryAsync(singleQueryable, options, true);

                return(SingleOrDefault(singleQueryable));
            }

            // response is a collection.
            var query = (value as IQueryable) ?? enumerable.AsQueryable();

            query = await ApplyQueryAsync(query, options, true);

            if (ODataCountMediaTypeMapping.IsCountRequest(options.Request.HttpContext))
            {
                long?count = options.Request.ODataFeature().TotalCount;

                if (count.HasValue)
                {
                    // Return the count value if it is a $count request.
                    return(count.Value);
                }
            }
            return(query);
        }
コード例 #9
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(ODataQueryOptions options, ODataValidationSettings validationSettings)
        {
            if (options == null)
            {
                throw Error.ArgumentNull("options");
            }

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

            // Validate each query options
            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.Count != null || ODataCountMediaTypeMapping.IsCountRequest(options.Request))
            {
                ValidateQueryOptionAllowed(AllowedQueryOptions.Count, validationSettings.AllowedQueryOptions);

                if (options.Count != null)
                {
                    options.Count.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);
            }
        }
コード例 #10
0
 /// <summary>
 /// Gets a value indicating if this is a count request.
 /// </summary>
 /// <returns></returns>
 public bool IsCountRequest()
 {
     return(ODataCountMediaTypeMapping.IsCountRequest(this.innerRequest.ODataProperties().Path));
 }
コード例 #11
0
ファイル: ODataQueryOptions.cs プロジェクト: ldcdev/WebApi
        /// <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);
        }
コード例 #12
0
        private object ExecuteQuery(object response, HttpRequestMessage request, HttpActionDescriptor actionDescriptor)
        {
            Type elementClrType = GetElementType(response, actionDescriptor);

            IEdmModel model = GetModel(elementClrType, request, actionDescriptor);

            if (model == null)
            {
                throw Error.InvalidOperation(SRResources.QueryGetModelMustNotReturnNull);
            }

            ODataQueryContext queryContext = new ODataQueryContext(
                model,
                elementClrType,
                request.ODataProperties().Path);
            ODataQueryOptions queryOptions = new ODataQueryOptions(queryContext, request);

            if (queryOptions.SelectExpand != null)
            {
                queryOptions.SelectExpand.LevelsMaxLiteralExpansionDepth = _validationSettings.MaxExpansionDepth;
            }

            ValidateQuery(request, queryOptions);

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

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

                SingleResult singleResult = response as SingleResult;
                if (singleResult == null)
                {
                    // response is a single entity.
                    return(ApplyQuery(entity: response, queryOptions: queryOptions));
                }
                else
                {
                    // response is a composable SingleResult. ApplyQuery and call SingleOrDefault.
                    IQueryable queryable = singleResult.Queryable;
                    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 (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(queryable);
            }
        }
コード例 #13
0
        /// <summary>
        /// Performs the query composition after action is executed. It first tries to retrieve the IQueryable from the
        /// returning response message. It then validates the query from uri based on the validation settings on
        /// <see cref="EnableQueryAttribute"/>. It finally applies the query appropriately, and reset it back on
        /// the response message.
        /// </summary>
        /// <param name="actionExecutedContext">The context related to this action, including the response message,
        /// request message and HttpConfiguration etc.</param>
        public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
        {
            if (actionExecutedContext == null)
            {
                throw Error.ArgumentNull("actionExecutedContext");
            }

            HttpRequestMessage request = actionExecutedContext.Request;

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

            HttpConfiguration configuration = request.GetConfiguration();

            if (configuration == null)
            {
                throw Error.Argument("actionExecutedContext", SRResources.RequestMustContainConfiguration);
            }

            if (actionExecutedContext.ActionContext == null)
            {
                throw Error.Argument("actionExecutedContext", SRResources.ActionExecutedContextMustHaveActionContext);
            }

            HttpActionDescriptor actionDescriptor = actionExecutedContext.ActionContext.ActionDescriptor;

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

            HttpResponseMessage response = actionExecutedContext.Response;

            if (response != null && response.IsSuccessStatusCode && response.Content != null)
            {
                ObjectContent responseContent = response.Content as ObjectContent;
                if (responseContent == null)
                {
                    throw Error.Argument("actionExecutedContext", SRResources.QueryingRequiresObjectContent,
                                         response.Content.GetType().FullName);
                }

                // Apply the query if there are any query options, if there is a page size set, in the case of
                // SingleResult or in the case of $count request.
                bool shouldApplyQuery = responseContent.Value != null &&
                                        request.RequestUri != null &&
                                        (!String.IsNullOrWhiteSpace(request.RequestUri.Query) ||
                                         _querySettings.PageSize.HasValue ||
                                         responseContent.Value is SingleResult ||
                                         ODataCountMediaTypeMapping.IsCountRequest(request));

                if (shouldApplyQuery)
                {
                    try
                    {
                        object queryResult = ExecuteQuery(responseContent.Value, request, actionDescriptor);
                        if (queryResult == null && request.ODataProperties().Path == null)
                        {
                            // This is the case in which a regular OData service uses the EnableQuery attribute.
                            // For OData services ODataNullValueMessageHandler should be plugged in for the service
                            // if this behavior is desired.
                            // For non OData services this behavior is equivalent as the one in the v3 version in order
                            // to reduce the friction when they decide to move to use the v4 EnableQueryAttribute.
                            actionExecutedContext.Response = request.CreateResponse(HttpStatusCode.NotFound);
                        }
                        else
                        {
                            responseContent.Value = queryResult;
                        }
                    }
                    catch (NotImplementedException e)
                    {
                        actionExecutedContext.Response = request.CreateErrorResponse(
                            HttpStatusCode.BadRequest,
                            Error.Format(SRResources.UriQueryStringInvalid, e.Message),
                            e);
                    }
                    catch (NotSupportedException e)
                    {
                        actionExecutedContext.Response = request.CreateErrorResponse(
                            HttpStatusCode.BadRequest,
                            Error.Format(SRResources.UriQueryStringInvalid, e.Message),
                            e);
                    }
                    catch (InvalidOperationException e)
                    {
                        // Will also catch ODataException here because ODataException derives from InvalidOperationException.
                        actionExecutedContext.Response = request.CreateErrorResponse(
                            HttpStatusCode.BadRequest,
                            Error.Format(SRResources.UriQueryStringInvalid, e.Message),
                            e);
                    }
                }
            }
        }
コード例 #14
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;
                    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;

                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 (ODataCountMediaTypeMapping.IsCountRequest(Request))
            {
                Count = new CountQueryOption(
                    "true",
                    Context,
                    new ODataQueryOptionParser(
                        Context.Model,
                        Context.ElementType,
                        Context.NavigationSource,
                        new Dictionary <string, string> {
                    { "$count", "true" }
                }));
            }
        }
コード例 #15
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, _assembliesResolver);
                Request.ODataProperties().ApplyClause = Apply.ApplyClause;
                this.Context.ElementClrType = Apply.ResultClrType;
            }

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

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

                if (ODataCountMediaTypeMapping.IsCountRequest(Request))
                {
                    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 = orderBy == null
                            ? GenerateDefaultOrderBy(Context)
                            : EnsureStableSortOrderBy(orderBy, Context);
            }

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

            AddAutoExpandProperties(querySettings);

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

            if (querySettings.PageSize.HasValue)
            {
                bool resultsLimited;
                result = LimitResults(result, querySettings.PageSize.Value, out resultsLimited);
                if (resultsLimited && Request.RequestUri != null && Request.RequestUri.IsAbsoluteUri && Request.ODataProperties().NextLink == null)
                {
                    Uri nextPageLink = Request.GetNextPageLink(querySettings.PageSize.Value);
                    Request.ODataProperties().NextLink = nextPageLink;
                }
            }

            return(result);
        }
コード例 #16
0
        public override void OnActionExecuted(ActionExecutedContext context)
        {
            if (context == null)
            {
                throw Error.ArgumentNull("context");
            }

            var response = context.Result as StatusCodeResult;

            if (response != null)// && !response.IsSuccessStatusCode())
            {
                return;
            }

            var request = context.HttpContext.Request;
            var model   = request.ODataFeature().Model;

            if (model == null)
            {
                throw Error.InvalidOperation(SRResources.QueryGetModelMustNotReturnNull);
            }

            var result = context.Result as ObjectResult;

            if (result == null)
            {
                if (context.Exception != null)
                {
                    throw context.Exception;
                }
                throw Error.Argument("context", SRResources.QueryingRequiresObjectContent, context.Result.GetType().FullName);
            }

            if (result?.Value is ODataError)
            {
                return;
            }

            var value = result.Value;

            if (request.GetDisplayUrl() == null || value == null ||
                value.GetType().GetTypeInfo().IsValueType || value is string)
            {
                return;
            }

            var elementClrType = result.GetElementType();
            var queryContext   = new ODataQueryContext(
                model,
                elementClrType,
                request.ODataFeature().Path);

            var shouldApplyQuery =
                request.HasQueryOptions() ||
                ResolvePageSize(_querySettings, context.ActionDescriptor).HasValue ||
                new InterceptorContainer(elementClrType, context.HttpContext.RequestServices).Any ||
                value is SingleResult ||
                ODataCountMediaTypeMapping.IsCountRequest(context.HttpContext) ||
                ContainsAutoExpandProperty(queryContext);

            if (!shouldApplyQuery)
            {
                return;
            }

            var queryOptions = new ODataQueryOptions(queryContext, request, context.HttpContext.RequestServices);

            long?count           = null;
            var  processedResult = ApplyQueryOptions(result.Value, queryOptions, context.ActionDescriptor);

            var enumberable = processedResult as IEnumerable <object>;

            if (enumberable != null)
            {
                // Apply count to result, if necessary, and return as a page result
                if (queryOptions.Count)
                {
                    count = request.ODataFeature().TotalCount;
                    count = Count(result.Value, queryOptions, context.ActionDescriptor);
                }
                // We might be getting a single result, so no paging involved
                var nextPageLink = request.ODataFeature().NextLink;
                var pageResult   = new PageResult <object>(enumberable, nextPageLink, count);
                result.Value = pageResult;
            }
            else
            {
                // Return just the single entity
                result.Value = processedResult;
            }
        }
コード例 #17
0
        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, _serviceProvider);
                    break;

                case "$orderby":
                    ThrowIfEmpty(kvp.Value, "$orderby");
                    RawValues.OrderBy = kvp.Value;
                    OrderBy           = new OrderByQueryOption(kvp.Value, Context, _queryOptionParser, _serviceProvider);
                    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);
                    SkipQueryOption = new SkipQueryOption(kvp.Value, Context, _queryOptionParser);
                    break;

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

                case "$apply":
                    ThrowIfEmpty(kvp.Value, "$apply");
                    RawValues.Apply = kvp.Value;
                    Apply           = new ApplyQueryOption(kvp.Value, Context, _queryOptionParser, _serviceProvider);
                    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, _serviceProvider);
                    break;

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

                case "$skiptoken":
                    RawValues.SkipToken = kvp.Value;
                    break;
                }
            }
            if (ODataCountMediaTypeMapping.IsCountRequest(Request.HttpContext))
            {
                CountQueryOption = new CountQueryOption(
                    "true",
                    Context,
                    new ODataQueryOptionParser(
                        Context.Model,
                        Context.ElementType,
                        Context.NavigationSource,
                        new Dictionary <string, string> {
                    { "$count", "true" }
                }));
            }
        }
コード例 #18
0
        /// <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="pageSize">The page size for this query</param>
        /// <param name="queryOptions"></param>
        /// <returns>The new <see cref="IQueryable"/> after the query has been applied to.</returns>
        public virtual IQueryable ApplyTo(IQueryable query, ODataQuerySettings querySettings, int?pageSize,
                                          ODataQueryOptions queryOptions)
        {
            if (query == null)
            {
                throw Error.ArgumentNull("query");
            }

            // Construct the actual query and apply them in the following order: filter
            if (Filter != null)
            {
                query = Filter.ApplyTo(query, querySettings, _assemblyProvider);
            }

            var 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 &&
                ((Skip != null && queryOptions.IgnoreSkip == false) ||
                 (Top != null && queryOptions.IgnoreTop == false) ||
                 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
                            ? OrderByHelper.GenerateDefaultOrderBy(Context, _serviceProvider)
                            : OrderByHelper.EnsureStableSortOrderBy(orderBy, Context, _serviceProvider);
            }

            // 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 (Apply != null)
            {
                query = Apply.ApplyTo(query, querySettings, _assemblyProvider);
                Request.ODataFeature().ApplyClause = Apply.ApplyClause;
                Context.ElementClrType = Apply.ResultClrType;
            }

            if (orderBy != null && Apply == null)
            {
                query = orderBy.ApplyTo(query, querySettings);
            }

            if (Skip.HasValue && queryOptions.IgnoreSkip == false)
            {
                query = ExpressionHelpers.Skip(query, Skip.Value, Context.ElementClrType, false);
            }

            int?take = null;

            if (querySettings.PageSize.HasValue)
            {
                take = Math.Min(querySettings.PageSize.Value, int.MaxValue);
            }
            if (Top.HasValue && queryOptions.IgnoreTop == false)
            {
                take = Math.Min(Top.Value, take ?? int.MaxValue);
            }
            if (take.HasValue)
            {
                query = ExpressionHelpers.Take(query, take.Value, Context.ElementClrType, false);
            }

            if (SelectExpand != null)
            {
                query = SelectExpand.ApplyTo(query, querySettings, _assemblyProvider);
            }

            if (CountQueryOption != null)
            {
                if (Request.ODataFeature().TotalCountFunc == null)
                {
                    Func <long> countFunc = CountQueryOption.GetEntityCountFunc(query);
                    if (countFunc != null)
                    {
                        Request.ODataFeature().TotalCountFunc = countFunc;
                    }
                }

                if (ODataCountMediaTypeMapping.IsCountRequest(Request.HttpContext))
                {
                    return(query);
                }
            }
            if (pageSize.HasValue && Apply == null)
            {
                bool resultsLimited;
                query = LimitResults(query, pageSize.Value, out resultsLimited);
                if (resultsLimited && Request.GetDisplayUrl() != null && new Uri(Request.GetDisplayUrl()).IsAbsoluteUri&& Request.ODataFeature().NextLink == null)
                {
                    Uri nextPageLink = Request.GetNextPageLink(pageSize.Value);
                    Request.ODataFeature().NextLink = nextPageLink;
                }
            }
            return(query);
        }
コード例 #19
0
        /// <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("query");
            }

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

            IQueryable result = query;

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

            if (Count != null)
            {
                if (Request.ODataProperties().TotalCount == null)
                {
                    long?count = Count.GetEntityCount(result);
                    if (count.HasValue)
                    {
                        Request.ODataProperties().TotalCount = count.Value;
                    }
                }

                if (ODataCountMediaTypeMapping.IsCountRequest(Request))
                {
                    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 &&
                (Skip != null || Top != null || 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 (orderBy != null)
            {
                result = orderBy.ApplyTo(result, querySettings);
            }

            if (Skip != null)
            {
                result = Skip.ApplyTo(result, querySettings);
            }

            if (Top != null)
            {
                result = Top.ApplyTo(result, querySettings);
            }

            if (SelectExpand != null)
            {
                SelectExpandClause      processedClause = SelectExpand.ProcessLevels();
                SelectExpandQueryOption newSelectExpand = new SelectExpandQueryOption(
                    SelectExpand.RawSelect,
                    SelectExpand.RawExpand,
                    SelectExpand.Context,
                    processedClause);

                Request.ODataProperties().SelectExpandClause = processedClause;
                result = newSelectExpand.ApplyTo(result, querySettings);
            }

            if (querySettings.PageSize.HasValue)
            {
                bool resultsLimited;
                result = LimitResults(result, querySettings.PageSize.Value, out resultsLimited);
                if (resultsLimited && Request.RequestUri != null && Request.RequestUri.IsAbsoluteUri && Request.ODataProperties().NextLink == null)
                {
                    Uri nextPageLink = GetNextPageLink(Request, querySettings.PageSize.Value);
                    Request.ODataProperties().NextLink = nextPageLink;
                }
            }

            return(result);
        }
コード例 #20
0
        public virtual async Task <object> ProcessQueryAsync(
            HttpRequest request,
            object value,
            Type elementClrType,
            bool ignoreSkip = false,
            bool ignoreTop  = false)
        {
            var model = request.ODataFeature().Model;

            if (request.GetDisplayUrl() == null || value == null ||
                value.GetType().GetTypeInfo().IsValueType || value is string)
            {
                return(value);
            }

            var queryContext = new ODataQueryContext(
                model,
                elementClrType,
                request.ODataFeature().Path);

            var shouldApplyQuery =
                request.HasQueryOptions() ||
                PageSize.HasValue ||
                new InterceptorContainer(elementClrType, request.HttpContext.RequestServices).Any ||
                value is SingleResult ||
                ODataCountMediaTypeMapping.IsCountRequest(request.HttpContext) ||
                ContainsAutoExpandProperty(queryContext, QuerySettings);

            if (!shouldApplyQuery)
            {
                return(value);
            }

            var queryOptions = new ODataQueryOptions(queryContext, request, request.HttpContext.RequestServices);

            if (ignoreSkip)
            {
                queryOptions.IgnoreSkip = true;
            }
            if (ignoreSkip)
            {
                queryOptions.IgnoreTop = true;
            }
            var processedResult = await ApplyQueryOptionsAsync(
                value,
                queryOptions);

            var enumberable = processedResult as IEnumerable <object>;

            if (enumberable != null)
            {
                long?count = null;
                // Apply count to result, if necessary, and return as a page result
                if (queryOptions.Count)
                {
                    //count = request.ODataFeature().TotalCount;
                    count = await Count(value, queryOptions);
                }

                // We might be getting a single result, so no paging involved
                var nextPageLink = request.ODataFeature().NextLink;
                var pageResult   = new PageResult <object>(enumberable, nextPageLink, count);
                value = pageResult;
            }
            else
            {
                // Return just the single entity
                value = processedResult;
            }

            return(value);
        }