public void CreateODataErrorFromExceptionArgsShouldCreateODataErrorWithCustomAnnotationsAndInstanceAnnotations()
 {
     DataServiceException dse = new DataServiceException(500, "500", "Test message", "en-US", null);
     HandleExceptionArgs args = new HandleExceptionArgs(dse, responseWritten:false, contentType:"application/json",verboseResponse:true);
     ODataError error = args.CreateODataError();
     error.InstanceAnnotations.As<object>().Should().BeSameAs(args.InstanceAnnotations);
 }
        private static DataServiceException SerializeAndDeserializeDataServiceException(DataServiceException sut)
        {
            var bf = new BinaryFormatter();
            var stream = new MemoryStream();

            bf.Serialize(stream, sut);
            stream.Seek(0, SeekOrigin.Begin);
            return (DataServiceException)bf.Deserialize(stream);
        }
Example #3
0
        /// <summary>
        /// Binds the expand paths from the requests $expand 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="expandQueryOption">The value of the $expand query option.</param>
        /// <returns>The bound expand segments.</returns>
        internal static IList <IList <ExpandItem> > BindExpandSegments(RequestDescription requestDescription, IDataService dataService, string expandQueryOption)
        {
            Debug.Assert(requestDescription != null, "requestDescription != null");
            Debug.Assert(dataService != null, "dataService != null");

            if (string.IsNullOrWhiteSpace(expandQueryOption))
            {
                return(new List <IList <ExpandItem> >());
            }

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

            if (targetResourceType == null || targetResourceType.ResourceTypeKind != ResourceTypeKind.EntityType || targetResourceSet == null)
            {
                throw DataServiceException.CreateBadRequestError(Strings.RequestQueryProcessor_QueryExpandOptionNotApplicable);
            }

            MetadataProviderEdmModel model      = dataService.Provider.GetMetadataProviderEdmModel();
            IEdmEntityType           targetType = (IEdmEntityType)model.EnsureSchemaType(targetResourceType);
            IEdmEntitySet            targetSet  = model.EnsureEntitySet(targetResourceSet);

            SelectExpandClause clause;

            try
            {
                model.Mode = MetadataProviderEdmModelMode.SelectAndExpandParsing;
                clause     = ODataUriParser.ParseSelectAndExpand(/*select*/ null, expandQueryOption, model, targetType, targetSet);
            }
            catch (ODataException ex)
            {
                throw new DataServiceException(400, null, ex.Message, null, ex);
            }
            finally
            {
                model.Mode = MetadataProviderEdmModelMode.Serialization;
            }

            return(new ExpandAndSelectPathExtractor(clause).ExpandPaths);
        }
        public void When_DataServiceException_is_serialized_all_public_instance_data_is_saved()
        {
            // Arrange
            const int expectedStatusCode = 705;
            const string expectedErrorCode = "A47";
            const string expectedMessage = "Server error.";
            const string expectedMessageLang = "en";

            var sut = new DataServiceException(
                expectedStatusCode, expectedErrorCode, expectedMessage, expectedMessageLang, innerException: null);

            // Act
            var ds = SerializeAndDeserializeDataServiceException(sut);

            // Assert
            Assert.AreNotSame(sut, ds);
            Assert.AreEqual(sut.GetType(), ds.GetType());
            Assert.AreEqual(expectedMessage, ds.Message);
            Assert.AreEqual(expectedStatusCode, ds.StatusCode);
            Assert.AreEqual(expectedErrorCode, ds.ErrorCode);
            Assert.AreEqual(expectedMessageLang, ds.MessageLanguage);
        }
Example #5
0
        /// <summary>
        /// update the request version header, if it is not specified.
        /// </summary>
        /// <param name="maxProtocolVersion">protocol version as specified in the config.</param>
        internal void InitializeRequestVersionHeaders(Version maxProtocolVersion)
        {
            if (this.requestVersionHeadersInitialized)
            {
                return;
            }

            this.requestVersionHeadersInitialized = true;

            Debug.Assert(this.requestVersion == null, "this.requestVersion == null");
            Debug.Assert(this.RequestMaxVersion == null, "this.RequestMaxVersion == null");

            Version maxRequestVersionAllowed = GetMaxRequestVersionAllowed(maxProtocolVersion);

            // read the request version headers from the underlying host
            this.requestVersionString = this.host.RequestVersion;
            this.RequestVersion       = ValidateVersionHeader(XmlConstants.HttpODataVersion, this.requestVersionString);
            this.RequestMaxVersion    = ValidateVersionHeader(XmlConstants.HttpODataMaxVersion, this.host.RequestMaxVersion);

            // If the request version is not specified.
            if (this.requestVersion == null)
            {
                // In request headers, if the OData-Version header is not specified and OData-MaxVersion is specified,
                // we should ideally set the OData-Version header to whatever the value was specified in OData-MaxVersion
                // header has.
                if (this.RequestMaxVersion != null)
                {
                    // set the OData-Version to minimum of OData-MaxVersion and ProtocolVersion.
                    this.RequestVersion = (this.RequestMaxVersion < maxProtocolVersion) ? this.RequestMaxVersion : maxProtocolVersion;
                }
                else
                {
                    // If both request DSV and request MaxDSV is not specified, then set the request DSV to the MPV
                    this.RequestVersion = maxProtocolVersion;
                }

                this.requestVersionString = this.RequestVersion.ToString(2);
            }
            else
            {
                if (this.RequestVersion > maxRequestVersionAllowed)
                {
                    throw DataServiceException.CreateBadRequestError(Strings.DataService_RequestVersionMustBeLessThanMPV(this.RequestVersion, maxProtocolVersion));
                }

                // Verify that the request DSV is a known version number.
                if (!VersionUtil.IsKnownRequestVersion(this.RequestVersion))
                {
                    string message = Strings.DataService_InvalidRequestVersion(
                        this.RequestVersion.ToString(2),
                        KnownODataVersionsToString(maxRequestVersionAllowed));
                    throw DataServiceException.CreateBadRequestError(message);
                }
            }

            // Initialize the request OData-MaxVersion if not specified.
            if (this.RequestMaxVersion == null)
            {
                this.RequestMaxVersion = maxProtocolVersion;
            }
            else if (this.RequestMaxVersion < VersionUtil.DataServiceDefaultResponseVersion)
            {
                // We need to make sure the MaxDSV is at least 1.0. This was the V1 behavior.
                // Verified that this was checked both in batch and non-batch cases.
                string message = Strings.DataService_MaxDSVTooLow(
                    this.RequestMaxVersion.ToString(2),
                    VersionUtil.DataServiceDefaultResponseVersion.Major,
                    VersionUtil.DataServiceDefaultResponseVersion.Minor);
                throw DataServiceException.CreateBadRequestError(message);
            }
        }
Example #6
0
        /// <summary>
        /// Parse the given etag value in the If-Match request header.
        /// </summary>
        /// <param name="etagProperties">List of etag properties for the type whose etag values we are parsing.</param>
        /// <param name="ifMatchHeaderValue">value of the If-Match header as specified in the request.</param>
        /// <returns>returns the etag value as a list containing the property name and its corresponding value. If the If-Match header value is '*', then returns an empty collection.</returns>
        private static IEnumerable <KeyValuePair <string, object> > ParseETagValue(IList <ResourceProperty> etagProperties, string ifMatchHeaderValue)
        {
            Debug.Assert(etagProperties != null && etagProperties.Count != 0, "There must be atleast one etag property specified");
            Debug.Assert(!String.IsNullOrEmpty(ifMatchHeaderValue), "IfMatch header cannot be null");

            if (ifMatchHeaderValue == XmlConstants.HttpAnyETag)
            {
                // if the value is '*', then we return an empty IEnumerable.
                return(WebUtil.EmptyKeyValuePairStringObject);
            }

            Debug.Assert(ifMatchHeaderValue.StartsWith(XmlConstants.HttpWeakETagPrefix, StringComparison.Ordinal), "If-Match header must be properly formatted - this check is done in DataService.CheckETagValues method");
            Debug.Assert(ifMatchHeaderValue.Length >= XmlConstants.HttpWeakETagPrefix.Length + 1, "If-Match header must be properly formatted - this check is done in DataService.CheckETagValues method");

            // Just get the etag value - we need to ignore the 'W/"' and the last '"' character from the etag
            string strippedETag = ifMatchHeaderValue.Substring(XmlConstants.HttpWeakETagPrefix.Length, ifMatchHeaderValue.Length - XmlConstants.HttpWeakETagPrefix.Length - 1);

            IList <object> etagValues = null;
            bool           success;
            Exception      innerException = null;

            // In V1, when we didn't have IConcurrencyProvider interface, we always used to compute the
            // latest etag from the entity instance we got from IUpdatable.GetResource and then comparing
            // it with the If-Match request header. Hence all invalid cases always used to throw
            // DataServiceException with 412, since the etags didn't match.
            // In V1.5, we have added the support for IConcurrencyProvider, which means we need to parse
            // the etag values and parse it to the provider, if it has implement this interface. To avoid
            // breaking changes, we need to catch all parsing errors and report them as 412 instead of 400
            // to avoid it from becoming a breaking change.
            try
            {
                success = SkipTokenAndETagParser.TryParseNullableTokens(Uri.UnescapeDataString(strippedETag), out etagValues);
            }
            catch (DataServiceException e)
            {
                success        = false;
                innerException = e;
            }

            if (!success)
            {
                // We could have throwed BadRequest here since the etag value is not properly formattted. But since
                // we used to do throw 412 in V1, keeping it that way to avoid breaking change.
                throw DataServiceException.CreatePreConditionFailedError(Strings.Serializer_ETagValueDoesNotMatch, innerException);
            }

            if (etagValues.Count != etagProperties.Count)
            {
                // We could have throwed BadRequest here since the etag value is not properly formattted. But since
                // we used to do throw 412 in V1, keeping it that way to avoid breaking change.
                throw DataServiceException.CreatePreConditionFailedError(Strings.Serializer_ETagValueDoesNotMatch);
            }

            KeyValuePair <string, object>[] etagPropertyInfo = new KeyValuePair <string, object> [etagProperties.Count];
            for (int i = 0; i < etagPropertyInfo.Length; i++)
            {
                ResourceProperty etagProperty  = etagProperties[i];
                object           propertyValue = null;
                string           value         = (string)etagValues[i];

                if (value != XmlConstants.NullLiteralInETag)
                {
                    // The reason we need to catch the Overflow Exception here is:
                    // In V1, when we didn't have IConcurrencyProvider interface, we always used to compute the
                    // latest etag from the entity instance we got from IUpdatable.GetResource and then comparing
                    // it with the If-Match request header. Hence all invalid cases always used to throw
                    // DataServiceException with 412, since the etags didn't match.
                    // In V1.5, we have added the support for IConcurrencyProvider, which means we need to parse
                    // the etag values and parse it to the provider, if it has implement this interface. To avoid
                    // breaking changes, we need to catch all parsing errors and report them as 412 instead of 400
                    // to avoid it from becoming a breaking change.
                    try
                    {
                        success = LiteralParser.ForETags.TryParseLiteral(etagProperty.Type, value, out propertyValue);
                    }
                    catch (OverflowException e)
                    {
                        success        = false;
                        innerException = e;
                    }

                    if (!success)
                    {
                        // We could have throwed BadRequest here since the etag value is not properly formattted. But since
                        // we used to do throw 412 in V1, keeping it that way to avoid breaking change.
                        throw DataServiceException.CreatePreConditionFailedError(Strings.Serializer_ETagValueDoesNotMatch, innerException);
                    }
                }

                etagPropertyInfo[i] = new KeyValuePair <string, object>(etagProperties[i].Name, propertyValue);
            }

            return(etagPropertyInfo);
        }
Example #7
0
        /// <summary>
        /// If the provider implements IConcurrencyProvider, then this method passes the etag values
        /// to the provider, otherwise compares the etag itself.
        /// </summary>
        /// <param name="resourceCookie">etag values for the given resource.</param>
        /// <param name="container">container for the given resource.</param>
        internal void SetETagValues(object resourceCookie, ResourceSetWrapper container)
        {
            Debug.Assert(resourceCookie != null, "resourceCookie != null");
            Debug.Assert(container != null, "container != null");
            AstoriaRequestMessage host = this.service.OperationContext.RequestMessage;

            Debug.Assert(String.IsNullOrEmpty(host.GetRequestIfNoneMatchHeader()), "IfNoneMatch header cannot be specified for Update/Delete operations");

            // Resolve the cookie first to the actual resource type
            object actualEntity = this.ResolveResource(resourceCookie);

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

            ResourceType resourceType = WebUtil.GetNonPrimitiveResourceType(this.service.Provider, actualEntity);

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

            IList <ResourceProperty> etagProperties = this.service.Provider.GetETagProperties(container.Name, resourceType);

            if (etagProperties.Count == 0)
            {
                if (!String.IsNullOrEmpty(host.GetRequestIfMatchHeader()))
                {
                    throw DataServiceException.CreateBadRequestError(Strings.Serializer_NoETagPropertiesForType);
                }

                // If the type has no etag properties, then we do not need to do any etag checks
                return;
            }

            // If the provider implements IConcurrencyProvider, then we need to call the provider
            // and pass the etag values. Else, we need to compare the etag values ourselves.
            IDataServiceUpdateProvider concurrencyProvider = this.updateProvider as IDataServiceUpdateProvider;

            if (concurrencyProvider != null)
            {
                bool?checkForEquality = null;
                IEnumerable <KeyValuePair <string, object> > etagValues;
                if (!String.IsNullOrEmpty(host.GetRequestIfMatchHeader()))
                {
                    checkForEquality = true;
                    etagValues       = ParseETagValue(etagProperties, host.GetRequestIfMatchHeader());
                }
                else
                {
                    etagValues = WebUtil.EmptyKeyValuePairStringObject;
                }

                concurrencyProvider.SetConcurrencyValues(resourceCookie, checkForEquality, etagValues);
            }
            else if (String.IsNullOrEmpty(host.GetRequestIfMatchHeader()))
            {
                throw DataServiceException.CreateBadRequestError(Strings.DataService_CannotPerformOperationWithoutETag(resourceType.FullName));
            }
            else if (host.GetRequestIfMatchHeader() != XmlConstants.HttpAnyETag)
            {
                // Compare If-Match header value with the current etag value, if the If-Match header value is not equal to '*'
                string etagValue = WebUtil.GetETagValue(resourceCookie, resourceType, etagProperties, this.service, false /*getMethod*/);
                Debug.Assert(!String.IsNullOrEmpty(etagValue), "etag value can never be null");

                if (etagValue != host.GetRequestIfMatchHeader())
                {
                    throw DataServiceException.CreatePreConditionFailedError(Strings.Serializer_ETagValueDoesNotMatch);
                }
            }
        }
Example #8
0
        /// <summary>
        /// Parses a single error element from the reader
        /// </summary>
        /// <param name="reader">The reader to read from.</param>
        /// <returns>The exception parsed.</returns>
        private static Exception ParseInStreamSingleError(XmlReader reader)
        {
            string type = null;
            string stackTrace = null;
            string message = null;
            Exception innerException = null;

            reader.Read();
            reader.Read();
            while (!reader.EOF)
            {
                if (reader.NodeType != XmlNodeType.Element ||
                    reader.NamespaceURI != UnitTestsUtil.MetadataNamespace.NamespaceName)
                {
                    reader.Read();
                    continue;
                }

                switch (reader.LocalName)
                {
                    case "message":
                        message = reader.ReadElementContentAsString(); break;
                    case "type":
                        type = reader.ReadElementContentAsString(); break;
                    case "stacktrace":
                        stackTrace = reader.ReadElementContentAsString(); break;
                    case "innererror":
                        innerException = ParseInStreamSingleError(reader.ReadSubtree());
                        break;
                    default:
                        reader.Skip();
                        break;
                }
            }

            Exception exception;
            switch (type)
            {
                case "System.InvalidOperationException":
                    exception = new InvalidOperationException(message, innerException);
                    break;
                default:
                    exception = new DataServiceException(message, innerException);
                    break;
            }

            exception.Data["StackTrace"] = stackTrace;
            return exception;
        }
Example #9
0
 /// <summary>Creates a new exception to indicate a syntax error.</summary>
 /// <param name="message">Plain text error message for this exception.</param>
 /// <returns>A new exception to indicate a syntax error.</returns>
 internal static DataServiceException CreateSyntaxError(string message)
 {
     return(DataServiceException.CreateBadRequestError(message));
 }
 /// <summary>Creates a new "Method Not Allowed" exception.</summary>
 /// <param name="message">Error message.</param>
 /// <param name="allow">String value for 'Allow' header in response.</param>
 /// <returns>A new exception to indicate the requested method is not allowed on the response.</returns>
 internal static DataServiceException CreateMethodNotAllowed(string message, string allow)
 {
     // 405 - Method Not Allowed
     DataServiceException result = new DataServiceException(405, message);
     result.state.ResponseAllowHeader = allow;
     return result;
 }
Example #11
0
 /// <summary>Creates a new "Bad Request" exception for recursion limit exceeded.</summary>
 /// <returns>A new exception to indicate that the request is rejected.</returns>
 internal static DataServiceException CreateDeepRecursion_General()
 {
     return(DataServiceException.CreateBadRequestError(Strings.BadRequest_DeepRecursion_General));
 }
Example #12
0
 /// <summary>Creates a new "Bad Request" exception for recursion limit exceeded.</summary>
 /// <param name="recursionLimit">Recursion limit that was reaced.</param>
 /// <returns>A new exception to indicate that the request is rejected.</returns>
 internal static DataServiceException CreateDeepRecursion(int recursionLimit)
 {
     return(DataServiceException.CreateBadRequestError(Strings.BadRequest_DeepRecursion(recursionLimit)));
 }
        /// <summary>Creates an <see cref="SegmentInfo"/> list for the given <paramref name="path"/>.</summary>
        /// <param name="path">Segments to process.</param>
        /// <returns>Segment information describing the given <paramref name="path"/>.</returns>
        internal IList <SegmentInfo> ConvertPath(ODataPath path)
        {
            Debug.Assert(path != null, "path != null");

            SegmentInfo        previous     = null;
            List <SegmentInfo> segmentInfos = new List <SegmentInfo>();
            bool crossReferencingUri        = false;

            foreach (ODataPathSegment segment in path)
            {
                crossReferencingUri |= segment is BatchReferenceSegment;

                SegmentInfo segmentInfo;
                if (previous == null)
                {
                    segmentInfo = this.CreateFirstSegment(segment);
                }
                else
                {
                    ThrowIfMustBeLeafSegment(previous);

                    var keySegment = segment as KeySegment;
                    if (keySegment != null)
                    {
                        // All service operations other than those returning IQueryable MUST NOT have a key expression.
                        if (!IsSegmentComposable(previous))
                        {
                            throw DataServiceException.CreateBadRequestError(Strings.RequestUriProcessor_SegmentDoesNotSupportKeyPredicates(previous.Identifier));
                        }

                        CheckSegmentIsComposable(previous);

                        if (crossReferencingUri)
                        {
                            throw DataServiceException.CreateBadRequestError(Strings.BadRequest_ResourceCanBeCrossReferencedOnlyForBindOperation);
                        }

                        previous.SingleResult = true;
                        previous.Key          = keySegment;
                        continue;
                    }

                    if (segment is NavigationPropertyLinkSegment)
                    {
                        segmentInfo = CreateEntityReferenceLinkSegment(previous);
        #if DEBUG
                        segmentInfo.AssertValid();
        #endif
                        segmentInfos.Add(segmentInfo);

                        previous = segmentInfo;
                    }

                    segmentInfo = this.CreateNextSegment(previous, segment);
                }

                Debug.Assert(segmentInfo != null, "segment != null");
#if DEBUG
                segmentInfo.AssertValid();
#endif
                segmentInfos.Add(segmentInfo);

                // we need to copy the segment over (even if it was an escape marker) as decisions will be made about it the next time through the loop.
                previous = segmentInfo;
            }

            return(segmentInfos);
        }
        /// <summary>Creates the first <see cref="SegmentInfo"/> for a request.</summary>
        /// <param name="segment">The text of the segment.</param>
        /// <returns>A description of the information on the segment.</returns>
        private SegmentInfo CreateFirstSegment(ODataPathSegment segment)
        {
            Debug.Assert(segment != null, "identifier != null");

            // Look for well-known system entry points.
            if (segment is MetadataSegment)
            {
                return(new SegmentInfo {
                    Identifier = XmlConstants.UriMetadataSegment, TargetKind = RequestTargetKind.Metadata
                });
            }

            if (segment is BatchSegment)
            {
                return(new SegmentInfo {
                    Identifier = XmlConstants.UriBatchSegment, TargetKind = RequestTargetKind.Batch
                });
            }

            if (segment is CountSegment)
            {
                // $count on root: throw
                throw DataServiceException.CreateResourceNotFound(Strings.RequestUriProcessor_CountOnRoot);
            }

            // Look for a service operation.
            OperationImportSegment serviceOperation = segment as OperationImportSegment;

            if (serviceOperation != null)
            {
                Debug.Assert(serviceOperation.OperationImports.Count() == 1, "Operation import segment should only ever have exactly one operation. Was a change made to how MetadataProviderEdmModel finds actions/service operations");
                var operationImport = serviceOperation.OperationImports.Single();
                var operation       = ((MetadataProviderEdmOperationImport)operationImport).ServiceOperation;
                Debug.Assert(operation != null, "operation != null");

                if (operation.Kind == OperationKind.ServiceOperation)
                {
                    return(CreateSegmentForServiceOperation(operation));
                }

                Debug.Assert(operation.Kind == OperationKind.Action, "serviceAction.Kind == OperationKind.Action");
                return(this.CreateSegmentForServiceAction(null /*previousSegment*/, operation));
            }

            var batchReferenceSegment = segment as BatchReferenceSegment;

            if (batchReferenceSegment != null)
            {
                SegmentInfo referencedSegmentInfo = this.crossReferenceCallback(batchReferenceSegment.ContentId);
                Debug.Assert(referencedSegmentInfo != null, "Could not find SegmentInfo for content-id: " + batchReferenceSegment.ContentId);
                referencedSegmentInfo.Identifier = batchReferenceSegment.ContentId;
                return(referencedSegmentInfo);
            }

            // Look for an entity set.
            EntitySetSegment entitySetSegment = segment as EntitySetSegment;

            if (entitySetSegment != null)
            {
                var container = ((IResourceSetBasedEdmEntitySet)entitySetSegment.EntitySet).ResourceSet;
                Debug.Assert(container != null, "container != null");
                SegmentInfo segmentInfo = new SegmentInfo
                {
                    Identifier         = container.Name,
                    TargetResourceSet  = container,
                    TargetResourceType = container.ResourceType,
                    TargetSource       = RequestTargetSource.EntitySet,
                    TargetKind         = RequestTargetKind.Resource,
                    SingleResult       = false
                };

                return(segmentInfo);
            }

            WebUtil.CheckResourceExists(false, segment.ToString());
            return(null);
        }
Example #15
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);
        }