/// <summary>
        /// Gets the value of a given item from the query string item collection.
        /// </summary>
        /// <param name="item">Name of the item to get from the query string.</param>
        /// <param name="collection">Collection of query string item/value pairs.</param>
        /// <returns>Value of the query item, or null if it is not present.</returns>
        internal static string GetQueryStringItem(string item, NameValueCollection collection)
        {
            string[] values = collection.GetValues(item);
            if (values == null || values.Length == 0)
            {
                // Do a scan of arguments ignoring whitespace.
                string keyFound = null;
                foreach (string key in collection.Keys)
                {
                    if (key != null && StringComparer.OrdinalIgnoreCase.Equals(key.Trim(), item))
                    {
                        if (keyFound != null)
                        {
                            throw DataServiceException.CreateBadRequestError(Strings.HttpContextServiceHost_AmbiguousItemName(item, keyFound, key));
                        }

                        keyFound = key;
                        values   = collection.GetValues(key);
                    }
                }

                if (values == null || values.Length == 0)
                {
                    return(null);
                }
            }

            Debug.Assert(values != null && values.Length > 0, "values != null && values.Length > 0 - otherwise we should have returned already");
            if (values.Length == 1)
            {
                return(values[0]);
            }

            throw DataServiceException.CreateSyntaxError();
        }
Beispiel #2
0
        /// <summary>
        /// Binds the paths from the request's $select query option to the sets/types/properties from the metadata provider of the service.
        /// </summary>
        /// <param name="requestDescription">The request description.</param>
        /// <param name="dataService">The data service.</param>
        /// <param name="selectQueryOption">The raw value of the $select query option.</param>
        /// <returns>The bound select segments.</returns>
        internal static IList <IList <SelectItem> > BindSelectSegments(RequestDescription requestDescription, IDataService dataService, string selectQueryOption)
        {
            Debug.Assert(requestDescription != null, "requestDescription != null");
            Debug.Assert(dataService != null, "dataService != null");

            if (string.IsNullOrEmpty(selectQueryOption))
            {
                return(new List <IList <SelectItem> >());
            }

            // Throw if $select requests have been disabled by the user
            Debug.Assert(dataService.Configuration != null, "dataService.Configuration != null");
            if (!dataService.Configuration.DataServiceBehavior.AcceptProjectionRequests)
            {
                throw DataServiceException.CreateBadRequestError(Strings.DataServiceConfiguration_ProjectionsNotAccepted);
            }

            IList <IList <string> > selectPathsAsText = SplitSelect(selectQueryOption, dataService.Provider);

            Debug.Assert(selectPathsAsText != null, "selectPathsAsText != null");

            List <IList <SelectItem> > boundSegments = new List <IList <SelectItem> >(selectPathsAsText.Count);

            if (selectPathsAsText.Count == 0)
            {
                return(boundSegments);
            }

            ValidateSelectIsAllowedForRequest(requestDescription);

            MetadataProviderEdmModel metadataProviderEdmModel = dataService.Provider.GetMetadataProviderEdmModel();

            for (int i = selectPathsAsText.Count - 1; i >= 0; i--)
            {
                IList <string>    path             = selectPathsAsText[i];
                List <SelectItem> boundSegmentPath = new List <SelectItem>(path.Count);
                boundSegments.Add(boundSegmentPath);

                ResourceType       targetResourceType = requestDescription.TargetResourceType;
                ResourceSetWrapper targetResourceSet  = requestDescription.TargetResourceSet;

                // if we get to here, we're building a partial selection
                List <TypeSegment> typeSegments   = new List <TypeSegment>();
                bool previousSegmentIsTypeSegment = false;
                for (int j = 0; j < path.Count; j++)
                {
                    string pathSegment     = path[j];
                    bool   lastPathSegment = (j == path.Count - 1);

                    // '*' is special, it means "Project all immediate properties on this level."
                    if (pathSegment == "*")
                    {
                        Debug.Assert(lastPathSegment, "A wildcard select segment must be the last one. This should have been checked already when splitting appart the paths.");
                        boundSegmentPath.Add(CreateWildcardSelection());
                        continue;
                    }

                    bool   nameIsContainerQualified;
                    string nameFromContainerQualifiedName = dataService.Provider.GetNameFromContainerQualifiedName(pathSegment, out nameIsContainerQualified);
                    if (nameFromContainerQualifiedName == "*")
                    {
                        Debug.Assert(lastPathSegment, "A wildcard select segment must be the last one.  This should have been checked already when splitting appart the paths.");
                        Debug.Assert(nameIsContainerQualified, "nameIsContainerQualified == true");
                        boundSegmentPath.Add(CreateContainerQualifiedWildcardSelection(metadataProviderEdmModel));
                        continue;
                    }

                    ResourceProperty property = targetResourceType.TryResolvePropertyName(pathSegment);
                    if (property == null)
                    {
                        ResourceType resolvedResourceType = WebUtil.ResolveTypeIdentifier(dataService.Provider, pathSegment, targetResourceType, previousSegmentIsTypeSegment);
                        if (resolvedResourceType != null)
                        {
                            previousSegmentIsTypeSegment = true;
                            if (lastPathSegment)
                            {
                                throw DataServiceException.CreateBadRequestError(Strings.RequestQueryProcessor_QueryParametersPathCannotEndInTypeIdentifier(XmlConstants.HttpQueryStringSelect, resolvedResourceType.FullName));
                            }

                            targetResourceType = resolvedResourceType;

                            // Whenever we encounter the type segment, we need to only verify that the MPV is set to 3.0 or higher.
                            // There is no need to check for request DSV, request MinDSV or request MaxDSV since there are no protocol changes in
                            // the payload for uri's with type identifier.
                            requestDescription.VerifyProtocolVersion(VersionUtil.Version3Dot0, dataService);

                            IEdmSchemaType edmType     = metadataProviderEdmModel.EnsureSchemaType(targetResourceType);
                            TypeSegment    typeSegment = new TypeSegment(edmType);
                            typeSegments.Add(typeSegment);
                            continue;
                        }

                        previousSegmentIsTypeSegment = false;

                        // If the currentResourceType is an open type, we require the service action name to be fully qualified or else we treat it as an open property name.
                        if (!targetResourceType.IsOpenType || nameIsContainerQualified)
                        {
                            // Note that if the service does not implement IDataServiceActionProvider and the MaxProtocolVersion < V3,
                            // GetActionsBoundToAnyTypeInResourceSet() would simply return an empty ServiceOperationWrapper collection.
                            Debug.Assert(dataService.ActionProvider != null, "dataService.ActionProvider != null");
                            IEnumerable <OperationWrapper> allOperationsInSet     = dataService.ActionProvider.GetActionsBoundToAnyTypeInResourceSet(targetResourceSet);
                            List <OperationWrapper>        selectedServiceActions = allOperationsInSet.Where(o => o.Name == nameFromContainerQualifiedName).ToList();

                            if (selectedServiceActions.Count > 0)
                            {
                                if (!lastPathSegment)
                                {
                                    throw DataServiceException.CreateBadRequestError(Strings.RequestQueryProcessor_ServiceActionMustBeLastSegmentInSelect(pathSegment));
                                }

                                boundSegmentPath.Add(CreateOperationSelection(metadataProviderEdmModel, selectedServiceActions, typeSegments));
                                continue;
                            }
                        }

                        if (!targetResourceType.IsOpenType)
                        {
                            throw DataServiceException.CreateSyntaxError(Strings.RequestUriProcessor_PropertyNotFound(targetResourceType.FullName, pathSegment));
                        }

                        if (!lastPathSegment)
                        {
                            // Open navigation properties are not supported on OpenTypes.
                            throw DataServiceException.CreateBadRequestError(Strings.OpenNavigationPropertiesNotSupportedOnOpenTypes(pathSegment));
                        }

                        boundSegmentPath.Add(CreateOpenPropertySelection(pathSegment, typeSegments));
                    }
                    else
                    {
                        previousSegmentIsTypeSegment = false;

                        ValidateSelectedProperty(targetResourceType, property, lastPathSegment);

                        boundSegmentPath.Add(CreatePropertySelection(metadataProviderEdmModel, targetResourceType, property, typeSegments));

                        if (property.TypeKind == ResourceTypeKind.EntityType)
                        {
                            targetResourceSet  = dataService.Provider.GetResourceSet(targetResourceSet, targetResourceType, property);
                            targetResourceType = property.ResourceType;
                        }
                    }
                }

                // Note that this check is also covering cases where a type segment is followed by a wildcard.
                if (previousSegmentIsTypeSegment)
                {
                    throw DataServiceException.CreateBadRequestError(Strings.RequestQueryProcessor_QueryParametersPathCannotEndInTypeIdentifier(XmlConstants.HttpQueryStringSelect, targetResourceType.FullName));
                }
            }

            return(boundSegments);
        }