RequestUriProcessor.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / ndp / fx / src / DataWeb / Server / System / Data / Services / RequestUriProcessor.cs / 2 / RequestUriProcessor.cs

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//  
//      Provides a class capable of processing Astoria request Uris.
//  
// 
// @owner  [....]
//--------------------------------------------------------------------- 

namespace System.Data.Services
{
    #region Namespaces. 

    using System; 
    using System.Collections; 
    using System.Collections.Generic;
#if ASTORIA_OPEN_OBJECT 
    using System.Data.Services.OpenTypes;
#endif
    using System.Data.Services.Parsing;
    using System.Data.Services.Providers; 
    using System.Diagnostics;
    using System.Linq; 
    using System.Linq.Expressions; 
    using System.Reflection;
 
    #endregion Namespaces.

    // Syntax for Astoria segments:
    // segment          ::= identifier [ query ] 
    // query            ::= "(" key ")"
    // key              ::= keyvalue *["," keyvalue] 
    // keyvalue         ::= *quotedvalue | *unquotedvalue 
    // quotedvalue      ::= "'" *qvchar "'"
    // qvchar           ::= char-except-quote | "''" 
    // unquotedvalue    ::= char

    /// 
    /// Use this class to process a web data service request Uri. 
    /// 
    internal static class RequestUriProcessor 
    { 
        /// MethodInfo for the RequestUriProcessor.InvokeWhere method.
        private static readonly MethodInfo InvokeWhereMethodInfo = typeof(RequestUriProcessor).GetMethod("InvokeWhere", BindingFlags.NonPublic | BindingFlags.Static); 

        /// Recursion limit on segment length.
        private const int RecursionLimit = 100;
 
        /// 
        /// Parses the request Uri that the host is exposing and returns 
        /// information about the intended results. 
        /// 
        /// Request uri that needs to get processed. 
        /// Data service for which the request is being processed.
        /// 
        /// An initialized RequestDescription instance describing what the
        /// request is for. 
        /// 
        ///  
        /// A  with status code 404 (Not Found) is returned if an identifier 
        /// in a segment cannot be resolved; 400 (Bad Request) is returned if a syntax
        /// error is found when processing a restriction (parenthesized text) or 
        /// in the query portion.
        /// 
        /// 
        /// Very important: no rights are checked on the last segment of the request. 
        /// 
        internal static RequestDescription ProcessRequestUri(Uri absoluteRequestUri, IDataService service) 
        { 
            Debug.Assert(service != null, "service != null");
            Debug.Assert(absoluteRequestUri != null, "absoluteRequestUri != null"); 
            Debug.Assert(absoluteRequestUri.IsAbsoluteUri, "absoluteRequestUri.IsAbsoluteUri(" + absoluteRequestUri + ")");

            string[] segments = EnumerateSegments(absoluteRequestUri, service.Host.AbsoluteServiceUri);
            if (segments.Length > RecursionLimit) 
            {
                throw DataServiceException.CreateBadRequestError(Strings.RequestUriProcessor_TooManySegments); 
            } 

            SegmentInfo[] segmentInfos = CreateSegments(segments, service); 
            SegmentInfo lastSegment = (segmentInfos.Length == 0) ? null : segmentInfos[segmentInfos.Length - 1];
            RequestTargetKind targetKind = (lastSegment == null) ? RequestTargetKind.ServiceDirectory : lastSegment.TargetKind;

            // Create a ResultDescription from the processed segments. 
            RequestDescription resultDescription;
            Uri resultUri = GetResultUri(service.Host); 
            if (targetKind == RequestTargetKind.Metadata || 
                targetKind == RequestTargetKind.Batch ||
                targetKind == RequestTargetKind.ServiceDirectory) 
            {
                resultDescription = new RequestDescription(targetKind, RequestTargetSource.None, resultUri);
            }
            else if (targetKind == RequestTargetKind.VoidServiceOperation) 
            {
                Debug.Assert(lastSegment != null, "lastSegment != null"); 
                Debug.Assert(lastSegment.TargetSource == RequestTargetSource.ServiceOperation, "targetSource == RequestTargetSource.ServiceOperation"); 
                lastSegment.Operation.Method.Invoke(service, lastSegment.OperationParameters);
                resultDescription = new RequestDescription( 
                    RequestTargetKind.VoidServiceOperation,     // targetKind
                    RequestTargetSource.ServiceOperation,       // targetSource
                    resultUri);                                 // resultUri
            } 
            else
            { 
                Debug.Assert(lastSegment != null, "lastSegment != null"); 
                Debug.Assert(
                    targetKind == RequestTargetKind.ComplexObject || 
#if ASTORIA_OPEN_OBJECT
                    targetKind == RequestTargetKind.OpenProperty ||
                    targetKind == RequestTargetKind.OpenPropertyValue ||
#endif 
                    targetKind == RequestTargetKind.Primitive ||
                    targetKind == RequestTargetKind.PrimitiveValue || 
                    targetKind == RequestTargetKind.Resource, 
                    "Known targetKind " + targetKind);
                RequestTargetSource targetSource = lastSegment.TargetSource; 
                ResourceProperty projectedProperty = lastSegment.ProjectedProperty;
                string containerName =
#if ASTORIA_OPEN_OBJECT
                    (lastSegment.TargetKind != RequestTargetKind.PrimitiveValue && 
                     lastSegment.TargetKind != RequestTargetKind.OpenPropertyValue) ?
#else 
                     lastSegment.TargetKind != RequestTargetKind.PrimitiveValue ? 
#endif
                     lastSegment.Identifier : 
                     segmentInfos[segmentInfos.Length - 2].Identifier;
                bool usesContainerName =
                    (targetSource == RequestTargetSource.Property &&
                     projectedProperty != null && 
                     projectedProperty.Kind != ResourcePropertyKind.ResourceSetReference) ||
                    (targetSource == RequestTargetSource.ServiceOperation && 
                     lastSegment.Operation.ResultKind == ServiceOperationResultKind.QueryWithSingleResult); 
                string mimeType =
                    (targetSource == RequestTargetSource.Property && projectedProperty != null) ? projectedProperty.MimeType : 
                    (targetSource == RequestTargetSource.ServiceOperation) ? lastSegment.Operation.MimeType :
                    null;
                resultDescription = new RequestDescription(
                    segmentInfos, 
                    containerName,
                    usesContainerName, 
                    mimeType, 
                    resultUri);
            } 

            // Process query options ($filter, $orderby, $expand, etc.)
            resultDescription = RequestQueryProcessor.ProcessQuery(service, resultDescription);
 
            return resultDescription;
        } 
 
        /// Appends a segment with the specified escaped .
        /// URI to append to. 
        /// Segment text, already escaped.
        /// A new URI with a new segment escaped.
        internal static Uri AppendEscapedSegment(Uri uri, string text)
        { 
            Debug.Assert(uri != null, "uri != null");
            Debug.Assert(text != null, "text != null"); 
 
            UriBuilder builder = new UriBuilder(uri);
            if (!builder.Path.EndsWith("/", StringComparison.Ordinal)) 
            {
                builder.Path += "/";
            }
 
            builder.Path += text;
            return builder.Uri; 
        } 

        /// Appends a segment with the specified unescaped . 
        /// URI to append to.
        /// Segment text, already escaped.
        /// A new URI with a new segment escaped.
        internal static Uri AppendUnescapedSegment(Uri uri, string text) 
        {
            Debug.Assert(uri != null, "uri != null"); 
            Debug.Assert(text != null, "text != null"); 
            return AppendEscapedSegment(uri, Uri.EscapeDataString(text));
        } 

        /// Gets the absolute URI that a reference (typically from a POST or PUT body) points to.
        /// Textual, URI-encoded reference.
        /// Request parameters with request and service URIs. 
        /// The absolute URI that  resolves to.
        internal static Uri GetAbsoluteUriFromReference(string reference, CachedRequestParams request) 
        { 
            return GetAbsoluteUriFromReference(reference, request.AbsoluteServiceUri);
        } 

        /// Gets the absolute URI that a reference (typically from a POST or PUT body) points to.
        /// Textual, URI-encoded reference.
        /// Absolure URI for service, used to validate that the URI points within. 
        /// The absolute URI that  resolves to.
        internal static Uri GetAbsoluteUriFromReference(string reference, Uri absoluteServiceUri) 
        { 
            Debug.Assert(!String.IsNullOrEmpty(reference), "!String.IsNullOrEmpty(reference) -- caller should check and throw appropriate message");
            Debug.Assert(absoluteServiceUri != null, "absoluteServiceUri != null"); 
            Debug.Assert(absoluteServiceUri.IsAbsoluteUri, "absoluteServiceUri.IsAbsoluteUri(" + absoluteServiceUri + ")");

            Uri referenceAsUri = new Uri(reference, UriKind.RelativeOrAbsolute);
            if (!referenceAsUri.IsAbsoluteUri) 
            {
                string slash = String.Empty; 
                if (absoluteServiceUri.OriginalString.EndsWith("/", StringComparison.Ordinal)) 
                {
                    if (reference.StartsWith("/", StringComparison.Ordinal)) 
                    {
                        reference = reference.Substring(1, reference.Length - 1);
                    }
                } 
                else if (!reference.StartsWith("/", StringComparison.Ordinal))
                { 
                    slash = "/"; 
                }
 
                referenceAsUri = new Uri(absoluteServiceUri.OriginalString + slash + reference);
            }

            if (!UriUtil.UriInvariantInsensitiveIsBaseOf(absoluteServiceUri, referenceAsUri)) 
            {
                string message = Strings.BadRequest_RequestUriDoesNotHaveTheRightBaseUri(referenceAsUri, absoluteServiceUri); 
                throw DataServiceException.CreateBadRequestError(message); 
            }
 
            Debug.Assert(
                referenceAsUri.IsAbsoluteUri,
                "referenceAsUri.IsAbsoluteUri(" + referenceAsUri + ") - otherwise composition from absolute yielded relative");
 
            return referenceAsUri;
        } 
 
        /// Gets the specified  as a string suitable for an HTTP request.
        ///  to get string for. 
        /// A string suitable for an HTTP request.
        internal static string GetHttpRequestUrl(Uri uri)
        {
            Debug.Assert(uri != null, "uri != null"); 
            if (uri.IsAbsoluteUri)
            { 
                return uri.GetComponents(UriComponents.HttpRequestUrl, UriFormat.UriEscaped); 
            }
            else 
            {
                return uri.OriginalString;
            }
        } 

        /// Gets the URI to the results, without the query component. 
        /// Host with request information. 
        /// The URI to the results, without the query component.
        internal static Uri GetResultUri(IDataServiceHost host) 
        {
            Debug.Assert(host != null, "host != null");
            Uri requestUri = GetAbsoluteRequestUri(host);
            UriBuilder resultBuilder = new UriBuilder(requestUri); 
            resultBuilder.Query = null;
 
            // This is fix for bug 565322. 
            // Since we don't allow uri to compose on collections, () must be present
            // as the last thing in the uri, if present. We need to remove the () from 
            // the uri, since its a optional thing and we want to return a canonical
            // uri from the server.
            if (resultBuilder.Path.EndsWith("()", StringComparison.Ordinal))
            { 
                resultBuilder.Path = resultBuilder.Path.Substring(0, resultBuilder.Path.Length - 2);
            } 
 
            return resultBuilder.Uri;
        } 

        /// Gets a non-null  for the hosted service.
        /// Host for the service.
        /// A non-null  for the hosted service. 
        internal static Uri GetAbsoluteRequestUri(IDataServiceHost host)
        { 
            Debug.Assert(host != null, "host != null"); 
            Uri serviceUri = host.AbsoluteRequestUri;
            if (serviceUri == null) 
            {
                throw new InvalidOperationException(Strings.RequestUriProcessor_AbsoluteRequestUriCannotBeNull);
            }
            else if (!serviceUri.IsAbsoluteUri) 
            {
                throw new InvalidOperationException(Strings.RequestUriProcessor_AbsoluteRequestUriMustBeAbsolute); 
            } 
            else
            { 
                return serviceUri;
            }
        }
 
        /// Gets a non-null  for the hosted service.
        /// Host for the service. 
        /// A non-null  for the hosted service. 
        internal static Uri GetServiceUri(IDataServiceHost host)
        { 
            Debug.Assert(host != null, "host != null");
            Uri serviceUri = host.AbsoluteServiceUri;
            if (serviceUri == null)
            { 
                throw new InvalidOperationException(Strings.RequestUriProcessor_AbsoluteServiceUriCannotBeNull);
            } 
            else if (!serviceUri.IsAbsoluteUri) 
            {
                throw new InvalidOperationException(Strings.RequestUriProcessor_AbsoluteServiceUriMustBeAbsolute); 
            }
            else
            {
                return serviceUri; 
            }
        } 
 
        /// 
        /// Given the uri, extract the key values from the uri 
        /// 
        /// uri from which the key values needs to be extracted
        /// Data context for which the request is being processed.
        /// returns the name of the source resource container name 
        /// key values as specified in the uri
        internal static KeyInstance ExtractKeyValuesFromUri(Uri absoluteRequestUri, IDataService service, out string containerName) 
        { 
            Debug.Assert(absoluteRequestUri != null, "absoluteRequestUri != null");
            Debug.Assert(absoluteRequestUri.IsAbsoluteUri, "absoluteRequestUri.IsAbsoluteUri(" + absoluteRequestUri + ")"); 
            Debug.Assert(service != null, "service != null");

            RequestDescription description = RequestUriProcessor.ProcessRequestUri(absoluteRequestUri, service);
 
            //
 
 
            if (description.TargetKind != RequestTargetKind.Resource || !description.IsSingleResult || description.TargetSource != RequestTargetSource.EntitySet)
            { 
                throw DataServiceException.CreateBadRequestError(Strings.BadRequestStream_MustSpecifyCanonicalUriInPayload(absoluteRequestUri));
            }

            // Dereferencing an object by specifying its key values implied the right to read it. 
            Debug.Assert(description.LastSegmentInfo.SingleResult, "description.LastSegmentInfo.SingleResult");
            service.Configuration.CheckResourceRightsForRead(description.LastSegmentInfo.TargetContainer, true /* singleResult */); 
 
            containerName = description.ContainerName;
 
            Debug.Assert(description.LastSegmentInfo.Key != null && !description.LastSegmentInfo.Key.IsEmpty, "Key Must be specified");
            return description.LastSegmentInfo.Key;
        }
 
        /// Invokes Queryable.Select for the specified query and selector.
        /// Query to invoke .Select method on. 
        /// Type that will be projected out. 
        /// Selector lambda expression.
        /// The resulting query. 
        internal static IQueryable InvokeSelectForTypes(IQueryable query, Type projectedType, LambdaExpression selector)
        {
            Debug.Assert(query != null, "query != null");
            Debug.Assert(projectedType != null, "projectedType != null"); 
            Debug.Assert(selector != null, "selector != null");
            MethodInfo method = typeof(RequestUriProcessor).GetMethod("InvokeSelect", BindingFlags.Static | BindingFlags.NonPublic); 
            method = method.MakeGenericMethod(query.ElementType, projectedType); 
            return (IQueryable)method.Invoke(null, new object[] { query, selector });
        } 

        /// Invokes Queryable.Where for the specified query and predicate.
        /// Query to invoke .Where method on.
        /// Predicate to pass as argument. 
        /// The resulting query.
        internal static IQueryable InvokeWhereForType(IQueryable query, LambdaExpression predicate) 
        { 
            Debug.Assert(query != null, "query != null");
            Debug.Assert(predicate != null, "predicate != null"); 
            MethodInfo whereMethod = InvokeWhereMethodInfo.MakeGenericMethod(query.ElementType);
            return (IQueryable)whereMethod.Invoke(null, new object[] { query, predicate });
        }
 
#if ASTORIA_OPEN_OBJECT
 
        ///  
        /// Composes the existing query on an open property segment to include the key.
        ///  
        /// Filter portion of segment.
        /// Target for the composition.
        private static void ComposeOpenPropertyQuery(string filter, SegmentInfo segment)
        { 
            Debug.Assert(filter != null, "filter != null");
            Debug.Assert(segment != null, "segment != null"); 
 
            segment.TargetKind = RequestTargetKind.OpenProperty;
            segment.Key = ExtractOpenPropertyKeyValues(filter); 
            segment.SingleResult = !segment.Key.IsEmpty;
            if (segment.SingleResult)
            {
                segment.RequestQueryable = SelectOpenResourceByKey(segment.RequestQueryable, segment.Key); 
            }
        } 
 
#endif
 
        /// Composes the filter portion of a segment onto the specifies query.
        /// Data provider that supplies metadata.
        /// Filter portion of segment, possibly null.
        /// Segment on which to compose. 
        private static void ComposeQuery(IDataServiceProvider provider, string filter, SegmentInfo segment)
        { 
            Debug.Assert(provider != null, "provider != null"); 
            Debug.Assert(filter != null, "filter != null");
            Debug.Assert(segment != null, "segment!= null"); 
            Debug.Assert(segment.SingleResult == false, "segment.SingleResult == false");

            ResourceType resourceType = provider.GetResourceType(segment.RequestQueryable.ElementType);
            segment.Key = ExtractKeyValues(resourceType, filter); 
            segment.SingleResult = !segment.Key.IsEmpty;
            segment.TargetKind = RequestTargetKind.Resource; 
            if (segment.SingleResult) 
            {
                if (!segment.Key.AreValuesNamed && segment.Key.ValueCount > 1) 
                {
                    throw DataServiceException.CreateBadRequestError(Strings.RequestUriProcessor_KeysMustBeNamed);
                }
 
                segment.RequestQueryable = SelectResourceByKey(segment.RequestQueryable, resourceType, segment.Key);
                segment.SingleResult = true; 
            } 
        }
 
        /// Creates the first  for a request.
        /// Service for which the request is being processed.
        /// Identifier portion of segment.
        /// Whether rights should be checked on this segment. 
        /// Query portion with key; possibly null.
        /// whether this segment references some other segment. 
        /// A description of the information on the segment. 
        private static SegmentInfo CreateFirstSegment(IDataService service, string identifier, bool checkRights, string queryPortion, out bool crossReferencingUrl)
        { 
            Debug.Assert(service != null, "service != null");
            Debug.Assert(identifier != null, "identifier != null");

            crossReferencingUrl = false; 
            SegmentInfo segment = new SegmentInfo();
            segment.Identifier = identifier; 
 
            // Look for well-known system entry points.
            if (segment.Identifier == XmlConstants.UriMetadataSegment) 
            {
                WebUtil.CheckSyntaxValid(queryPortion == null);
                segment.TargetKind = RequestTargetKind.Metadata;
                return segment; 
            }
 
            if (segment.Identifier == XmlConstants.UriBatchSegment) 
            {
                WebUtil.CheckSyntaxValid(queryPortion == null); 
                segment.TargetKind = RequestTargetKind.Batch;
                return segment;
            }
 
            // Look for a service operation.
            segment.Operation = service.Provider.TryResolveServiceOperation(segment.Identifier); 
            if (segment.Operation != null) 
            {
                WebUtil.DebugEnumIsDefined(segment.Operation.ResultKind); 
                segment.TargetSource = RequestTargetSource.ServiceOperation;
                string requiredMethod = segment.Operation.Invoke ? XmlConstants.HttpMethodPost : XmlConstants.HttpMethodGet;
                if (service.Host.RequestHttpMethod != requiredMethod)
                { 
                    throw DataServiceException.CreateMethodNotAllowed(Strings.RequestUriProcessor_MethodNotAllowed, requiredMethod);
                } 
 
                segment.TargetContainer = segment.Operation.EntitySet;
                segment.TargetElementType = segment.Operation.ResultType; 
                segment.OperationParameters = ReadOperationParameters(service.Host, segment.Operation);
                switch (segment.Operation.ResultKind)
                {
                    case ServiceOperationResultKind.QueryWithMultipleResults: 
                    case ServiceOperationResultKind.QueryWithSingleResult:
                        segment.RequestQueryable = (IQueryable)segment.Operation.Method.Invoke(service, segment.OperationParameters); 
                        WebUtil.CheckResourceExists(segment.RequestQueryable != null, segment.Identifier); 
                        segment.SingleResult = (segment.Operation.ResultKind == ServiceOperationResultKind.QueryWithSingleResult);
                        break; 

                    case ServiceOperationResultKind.DirectValue:
                    case ServiceOperationResultKind.Enumeration:
                        object methodResult; 
                        try
                        { 
                            methodResult = segment.Operation.Method.Invoke(service, segment.OperationParameters); 
                        }
                        catch (TargetInvocationException exception) 
                        {
                            ErrorHandler.HandleTargetInvocationException(exception);
                            throw;
                        } 

                        segment.SingleResult = (segment.Operation.ResultKind == ServiceOperationResultKind.DirectValue); 
                        WebUtil.CheckResourceExists(segment.SingleResult || methodResult != null, segment.Identifier);   // Enumerations shouldn't be null. 
                        segment.RequestEnumerable = segment.SingleResult ? new object[1] { methodResult } : (IEnumerable)methodResult;
                        segment.TargetElementType = segment.Operation.ResultType; 
                        segment.TargetKind = TargetKindFromType(segment.TargetElementType, service.Provider);
                        WebUtil.CheckSyntaxValid(queryPortion == null);
                        RequestQueryProcessor.CheckEmptyQueryArguments(service);
                        break; 

                    default: 
                        Debug.Assert(segment.Operation.ResultKind == ServiceOperationResultKind.Nothing, "segment.Operation.ResultKind == ServiceOperationResultKind.Nothing"); 
                        segment.TargetKind = RequestTargetKind.VoidServiceOperation;
                        break; 
                }

                if (segment.RequestQueryable != null)
                { 
                    segment.TargetKind = TargetKindFromType(segment.RequestQueryable.ElementType, service.Provider);
                    if (queryPortion != null) 
                    { 
                        WebUtil.CheckSyntaxValid(!segment.SingleResult);
                        ComposeQuery(service.Provider, queryPortion, segment); 
                    }
                }

                if (checkRights) 
                {
                    service.Configuration.CheckServiceRights(segment.Operation, segment.SingleResult); 
                } 

                return segment; 
            }

            SegmentInfo newSegmentInfo = service.GetSegmentForContentId(segment.Identifier);
            if (newSegmentInfo != null) 
            {
                newSegmentInfo.Identifier = segment.Identifier; 
                crossReferencingUrl = true; 
                return newSegmentInfo;
            } 

            // Look for an entity set.
            ResourceContainer container = service.Provider.TryResolveContainerName(segment.Identifier);
            WebUtil.CheckResourceExists(container != null, segment.Identifier); 
            segment.RequestQueryable = service.Provider.ResolveContainerName(segment.Identifier);
            WebUtil.CheckResourceExists(segment.RequestQueryable != null, segment.Identifier); 
            segment.TargetContainer = container; 
            segment.TargetElementType = container.ElementType;
            segment.TargetSource = RequestTargetSource.EntitySet; 
            segment.TargetKind = RequestTargetKind.Resource;
            segment.SingleResult = false;
            if (queryPortion != null)
            { 
                ComposeQuery(service.Provider, queryPortion, segment);
            } 
 
            if (checkRights)
            { 
                service.Configuration.CheckResourceRightsForRead(container, segment.SingleResult);
            }

            segment.RequestQueryable = DataServiceConfiguration.ComposeResourceContainer(service, container, segment.RequestQueryable); 
            return segment;
        } 
 
        /// Creates a  array for the given .
        /// Segments to process. 
        /// Service for which segments are being processed.
        /// Segment information describing the given .
        private static SegmentInfo[] CreateSegments(string[] segments, IDataService service)
        { 
            Debug.Assert(segments != null, "segments != null");
            Debug.Assert(service != null, "service != null"); 
            SegmentInfo previous = null; 
            SegmentInfo[] segmentInfos = new SegmentInfo[segments.Length];
            bool crossReferencingUri = false; 
            bool postLinkSegment = false;
            for (int i = 0; i < segments.Length; i++)
            {
                string segmentText = segments[i]; 
                bool checkRights = (i != segments.Length - 1);  // Whether rights should be checked on this segment.
                string identifier; 
                bool hasQuery = ExtractSegmentIdentifier(segmentText, out identifier); 
                string queryPortion = hasQuery ? segmentText.Substring(identifier.Length) : null;
                SegmentInfo segment; 

                // We allow a single trailing '/', which results in an empty segment.
                // However System.Uri removes it, so any empty segment we see is a 404 error.
                if (identifier.Length == 0) 
                {
                    throw DataServiceException.ResourceNotFoundError(Strings.RequestUriProcessor_EmptySegmentInRequestUrl); 
                } 

                if (previous == null) 
                {
                    segment = CreateFirstSegment(service, identifier, checkRights, queryPortion, out crossReferencingUri);
                }
                else if (previous.TargetKind == RequestTargetKind.Batch || 
                        previous.TargetKind == RequestTargetKind.Metadata ||
                        previous.TargetKind == RequestTargetKind.PrimitiveValue || 
                        previous.TargetKind == RequestTargetKind.VoidServiceOperation) 
                {
                    // Nothing can come after a $metadata, $value or $batch segment. 
                    // Nothing can come after a service operation with void return type.
                    throw DataServiceException.ResourceNotFoundError(Strings.RequestUriProcessor_MustBeLeafSegment(previous.Identifier));
                }
                else if (postLinkSegment) 
                {
                    throw DataServiceException.ResourceNotFoundError(Strings.RequestUriProcessor_CannotSpecifyAfterPostLinkSegment(identifier, XmlConstants.UriLinkSegment)); 
                } 
                else if (previous.TargetKind == RequestTargetKind.Primitive)
                { 
                    // $value is the well-known identifier to return a primitive property
                    // in its natural MIME format.
                    if (identifier != XmlConstants.UriValueSegment)
                    { 
                        throw DataServiceException.ResourceNotFoundError(Strings.RequestUriProcessor_ValueSegmentAfterScalarPropertySegment(previous.Identifier, identifier));
                    } 
 
                    WebUtil.CheckSyntaxValid(!hasQuery);
                    segment = new SegmentInfo(previous); 
                    segment.Identifier = identifier;
                    segment.SingleResult = true;
                    segment.TargetKind = RequestTargetKind.PrimitiveValue;
                } 
                else if ((
#if ASTORIA_OPEN_OBJECT 
                        previous.TargetKind == RequestTargetKind.OpenProperty || 
#endif
                          previous.TargetKind == RequestTargetKind.Resource) && 
                          previous.SingleResult &&
                          identifier == XmlConstants.UriLinkSegment)
                {
                    segment = new SegmentInfo(previous); 
                    segment.Identifier = identifier;
                    segment.TargetKind = RequestTargetKind.Link; 
                } 
                else
                { 
                    Debug.Assert(
                        previous.TargetKind == RequestTargetKind.ComplexObject ||
                        previous.TargetKind == RequestTargetKind.Resource ||
#if ASTORIA_OPEN_OBJECT 
                        previous.TargetKind == RequestTargetKind.OpenProperty ||
#endif 
                        previous.TargetKind == RequestTargetKind.Link, 
                        "previous.TargetKind(" + previous.TargetKind + ") can have properties");
 
                    postLinkSegment = (previous.TargetKind == RequestTargetKind.Link);

                    // Enumerable results cannot be composed at all.
                    if (previous.Operation != null && 
                        previous.Operation.ResultKind == ServiceOperationResultKind.Enumeration)
                    { 
                        throw DataServiceException.ResourceNotFoundError( 
                            Strings.RequestUriProcessor_IEnumerableServiceOperationsCannotBeFurtherComposed(previous.Identifier));
                    } 

                    if (!previous.SingleResult)
                    {
                        throw DataServiceException.CreateBadRequestError( 
                            Strings.RequestUriProcessor_CannotQueryCollections(previous.Identifier));
                    } 
 
                    segment = new SegmentInfo();
                    segment.Identifier = identifier; 
                    segment.TargetSource = RequestTargetSource.Property;

                    // A segment will correspond to a property in the object model;
                    // if we are processing an open type, anything further in the 
                    // URI also represents an open type property.
#if ASTORIA_OPEN_OBJECT 
                    if (previous.TargetElementType == typeof(object)) 
                    {
                        Debug.Assert(previous.TargetKind == RequestTargetKind.OpenProperty, "For open properties, the target element type must be object"); 
                        segment.ProjectedProperty = null;
                    }
                    else
#endif 
                    {
#if ASTORIA_OPEN_OBJECT 
                        Debug.Assert(previous.TargetKind != RequestTargetKind.OpenProperty, "Since the query element type is known, this can't be open property"); 
#endif
                        segment.ProjectedProperty = service.Provider.TryResolvePropertyName(previous.TargetElementType, identifier); 
#if !ASTORIA_OPEN_OBJECT
                        WebUtil.CheckResourceExists(segment.ProjectedProperty != null, identifier);
#endif
                    } 

#if ASTORIA_OPEN_OBJECT 
                    if (identifier == XmlConstants.UriValueSegment && previous.TargetKind == RequestTargetKind.OpenProperty) 
                    {
                        segment.RequestEnumerable = previous.RequestEnumerable; 
                        segment.SingleResult = true;
                        segment.TargetKind = RequestTargetKind.OpenPropertyValue;
                        segment.TargetElementType = previous.TargetElementType;
                    } 
                    else if (segment.ProjectedProperty == null)
                    { 
                        // Handle an open type property. If the current leaf isn't an 
                        // object (which implies it's already an open type), then
                        // it should be marked as an open type. 
                        if (previous.TargetElementType != typeof(object))
                        {
                            ResourceType resourceType = service.Provider.GetResourceType(previous.TargetElementType);
                            Debug.Assert(resourceType != null, "resourceType != null -- otherwise WebUtil.CheckResourceExists should have thrown."); 
                            WebUtil.CheckResourceExists(resourceType.IsOpenType, segment.Identifier);
                        } 
 
                        segment.TargetElementType = typeof(object);
                        segment.TargetKind = RequestTargetKind.OpenProperty; 
                        segment.SingleResult = !hasQuery;

                        if (!crossReferencingUri)
                        { 
                            if (hasQuery)
                            { 
                                segment.RequestQueryable = SelectOpenPropertyMultiple(previous.RequestQueryable, identifier); 
                                ComposeOpenPropertyQuery(queryPortion, segment);
                            } 
                            else
                            {
                                segment.RequestQueryable = SelectOpenProperty(previous.RequestQueryable, identifier);
                            } 
                        }
                    } 
                    else 
#endif
                    { 
                        // Handle a strongly-typed property.
                        segment.TargetContainer = segment.ProjectedProperty.ResourceContainer;
                        segment.TargetElementType = segment.ProjectedProperty.ResourceClrType;
                        ResourcePropertyKind propertyKind = segment.ProjectedProperty.Kind; 
                        segment.SingleResult = (propertyKind != ResourcePropertyKind.ResourceSetReference);
                        if (!crossReferencingUri) 
                        { 
                            segment.RequestQueryable = segment.SingleResult ?
                                SelectElement(previous.RequestQueryable, segment.ProjectedProperty) : 
                                SelectMultiple(previous.RequestQueryable, segment.ProjectedProperty);
                        }

                        if (previous.TargetKind == RequestTargetKind.Link && 
                            segment.ProjectedProperty.TypeKind != ResourceTypeKind.EntityType)
                        { 
                            throw DataServiceException.CreateBadRequestError(Strings.RequestUriProcessor_LinkSegmentMustBeFollowedByEntitySegment(identifier, XmlConstants.UriLinkSegment)); 
                        }
 
                        switch (propertyKind)
                        {
                            case ResourcePropertyKind.ComplexType:
                                segment.TargetKind = RequestTargetKind.ComplexObject; 
                                break;
                            case ResourcePropertyKind.ResourceReference: 
                            case ResourcePropertyKind.ResourceSetReference: 
                                segment.TargetKind = RequestTargetKind.Resource;
                                segment.TargetContainer = service.Provider.GetContainer( 
                                    previous.TargetContainer.Name, previous.TargetElementType, segment.ProjectedProperty);
                                Debug.Assert(
                                    segment.TargetContainer != null,
                                    "segment.TargetContainer != null -- for strongly typed navigation properties, we should always know the target container"); 
                                break;
                            default: 
                                Debug.Assert(segment.ProjectedProperty.IsOfKind(ResourcePropertyKind.Primitive), "must be primitive type property"); 
                                segment.TargetKind = RequestTargetKind.Primitive;
                                break; 
                        }

                        if (hasQuery)
                        { 
                            WebUtil.CheckSyntaxValid(!segment.SingleResult);
                            if (!crossReferencingUri) 
                            { 
                                ComposeQuery(service.Provider, queryPortion, segment);
                            } 
                            else
                            {
                                throw DataServiceException.CreateBadRequestError(Strings.BadRequest_ResourceCanBeCrossReferencedOnlyForBindOperation);
                            } 
                        }
 
                        // Do security checks and authorization query composition. 
                        if (segment.TargetContainer != null)
                        { 
                            if (checkRights)
                            {
                                service.Configuration.CheckResourceRightsForRead(segment.TargetContainer, segment.SingleResult);
                            } 

                            if (!crossReferencingUri) 
                            { 
                                segment.RequestQueryable = DataServiceConfiguration.ComposeResourceContainer(service, segment.TargetContainer, segment.RequestQueryable);
                            } 
                        }
                    }
                }
 
#if DEBUG
                segment.AssertValid(); 
#endif 
                segmentInfos[i] = segment;
                previous = segment; 
            }

            if (segments.Length != 0 && previous.TargetKind == RequestTargetKind.Link)
            { 
                throw DataServiceException.CreateBadRequestError(Strings.RequestUriProcessor_MissingSegmentAfterLink(XmlConstants.UriLinkSegment));
            } 
 
            return segmentInfos;
        } 

        /// 
        /// Returns an object that can enumerate the segments in the specified path (eg: /foo/bar -> foo, bar).
        ///  
        /// A valid path portion of an uri.
        /// baseUri for the request that is getting processed. 
        /// An enumerable object of unescaped segments. 
        private static string[] EnumerateSegments(Uri absoluteRequestUri, Uri baseUri)
        { 
            Debug.Assert(absoluteRequestUri != null, "absoluteRequestUri != null");
            Debug.Assert(absoluteRequestUri.IsAbsoluteUri, "absoluteRequestUri.IsAbsoluteUri(" + absoluteRequestUri.IsAbsoluteUri + ")");
            Debug.Assert(baseUri != null, "baseUri != null");
            Debug.Assert(baseUri.IsAbsoluteUri, "baseUri.IsAbsoluteUri(" + baseUri + ")"); 

            if (!UriUtil.UriInvariantInsensitiveIsBaseOf(baseUri, absoluteRequestUri)) 
            { 
                throw DataServiceException.CreateBadRequestError(Strings.BadRequest_RequestUriDoesNotHaveTheRightBaseUri(absoluteRequestUri, baseUri));
            } 

            try
            {
                // 
                Uri uri = absoluteRequestUri;
                int numberOfSegmentsToSkip = 0; 
 
                // Since there is a svc part in the segment, we will need to skip 2 segments
                numberOfSegmentsToSkip = baseUri.Segments.Length; 

                string[] uriSegments = uri.Segments;
                int populatedSegmentCount = 0;
                for (int i = numberOfSegmentsToSkip; i < uriSegments.Length; i++) 
                {
                    string segment = uriSegments[i]; 
                    if (segment.Length != 0 && segment != "/") 
                    {
                        populatedSegmentCount++; 
                    }
                }

                string[] segments = new string[populatedSegmentCount]; 
                int segmentIndex = 0;
                for (int i = numberOfSegmentsToSkip; i < uriSegments.Length; i++) 
                { 
                    string segment = uriSegments[i];
                    if (segment.Length != 0 && segment != "/") 
                    {
                        if (segment[segment.Length - 1] == '/')
                        {
                            segment = segment.Substring(0, segment.Length - 1); 
                        }
 
                        segments[segmentIndex++] = Uri.UnescapeDataString(segment); 
                    }
                } 

                Debug.Assert(segmentIndex == segments.Length, "segmentIndex == segments.Length -- otherwise we mis-counted populated/skipped segments.");
                return segments;
            } 
            catch (UriFormatException)
            { 
                throw DataServiceException.CreateSyntaxError(); 
            }
        } 

        /// Returns an object that can enumerate key values.
        /// resource type whose keys need to be extracted
        /// Key (query) part of an Astoria segment. 
        /// An object that can enumerate key values.
        private static KeyInstance ExtractKeyValues(ResourceType resourceType, string filter) 
        { 
            KeyInstance key;
            filter = RemoveFilterParens(filter); 
            WebUtil.CheckSyntaxValid(KeyInstance.TryParseFromUri(filter, out key));

            if (!key.IsEmpty)
            { 
                // Make sure the keys specified in the uri matches with the number of keys in the metadata
                if (resourceType.KeyProperties.Count != key.ValueCount) 
                { 
                    throw DataServiceException.CreateBadRequestError(Strings.BadRequest_KeyCountMismatch(resourceType.FullName));
                } 

                WebUtil.CheckSyntaxValid(key.TryConvertValues(resourceType));
            }
 
            return key;
        } 
 
        /// Returns an object that can enumerate key values for an open property.
        /// Key (query) part of an Astoria segment. 
        /// An object that can enumerate key values.
        private static KeyInstance ExtractOpenPropertyKeyValues(string filter)
        {
            KeyInstance key; 
            filter = RemoveFilterParens(filter);
            WebUtil.CheckSyntaxValid(KeyInstance.TryParseFromUri(filter, out key)); 
            key.RemoveQuotes(); 
            return key;
        } 

        /// Extracts the identifier part of the unescaped Astoria segment.
        /// Unescaped Astoria segment.
        /// On returning, the identifier in the segment. 
        /// true if keys follow the identifier.
        private static bool ExtractSegmentIdentifier(string segment, out string identifier) 
        { 
            Debug.Assert(segment != null, "segment != null");
 
            int filterStart = 0;
            while (filterStart < segment.Length && segment[filterStart] != '(')
            {
                filterStart++; 
            }
 
            identifier = segment.Substring(0, filterStart); 
            return filterStart < segment.Length;
        } 

        /// Generic method to invoke a Select method on an IQueryable source.
        /// Element type of the source.
        /// Result type of the projection. 
        /// Source query.
        /// Lambda expression that turns TSource into TResult. 
        /// A new query that projects TSource into TResult. 
        private static IQueryable InvokeSelect(IQueryable source, LambdaExpression selector)
        { 
            Debug.Assert(source != null, "source != null");
            Debug.Assert(selector != null, "selector != null");

            IQueryable typedSource = (IQueryable)source; 
            Expression> typedSelector = (Expression>)selector;
            return Queryable.Select(typedSource, typedSelector); 
        } 

        /// Generic method to invoke a SelectMany method on an IQueryable source. 
        /// Element type of the source.
        /// Result type of the projection.
        /// Source query.
        /// Lambda expression that turns TSource into IEnumerable<TResult>. 
        /// A new query that projects TSource into IEnumerable<TResult>.
        private static IQueryable InvokeSelectMany(IQueryable source, LambdaExpression selector) 
        { 
            Debug.Assert(source != null, "source != null");
            Debug.Assert(selector != null, "selector != null"); 

            IQueryable typedSource = (IQueryable)source;
            Expression>> typedSelector = (Expression>>)selector;
            return Queryable.SelectMany(typedSource, typedSelector); 
        }
 
        /// Generic method to invoke a Where method on an IQueryable source. 
        /// Element type of the source.
        /// Source query. 
        /// Lambda expression that filters the result of the query.
        /// A new query that filters the query.
        private static IQueryable InvokeWhere(IQueryable query, LambdaExpression predicate)
        { 
            Debug.Assert(query != null, "query != null");
            Debug.Assert(predicate != null, "predicate != null"); 
 
            IQueryable typedQueryable = (IQueryable)query;
            Expression> typedPredicate = (Expression>)predicate; 
            return Queryable.Where(typedQueryable, typedPredicate);
        }

        ///  
        /// Reads the parameters for the specified  from the
        /// . 
        ///  
        /// Host with request information.
        /// Operation with parameters to be read. 
        /// A new object[] with parameter values.
        private static object[] ReadOperationParameters(IDataServiceHost host, ServiceOperation operation)
        {
            Debug.Assert(host != null, "host != null"); 

            object[] operationParameters = new object[operation.Parameters.Length]; 
            for (int i = 0; i < operation.Parameters.Length; i++) 
            {
                Type parameterType = operation.Parameters[i].Type; 
                string queryStringValue = host.GetQueryStringItem(operation.Parameters[i].Name);
                Type underlyingType = Nullable.GetUnderlyingType(parameterType);
                if (String.IsNullOrEmpty(queryStringValue))
                { 
                    WebUtil.CheckSyntaxValid(parameterType.IsClass || underlyingType != null);
                    operationParameters[i] = null; 
                } 
                else
                { 
                    // We choose to be a little more flexible than with keys and
                    // allow surrounding whitespace (which is never significant).
                    // See SQLBUDT #555944.
                    queryStringValue = queryStringValue.Trim(); 
                    Type targetType = underlyingType ?? parameterType;
                    if (WebConvert.IsKeyTypeQuoted(targetType)) 
                    { 
                        WebUtil.CheckSyntaxValid(WebConvert.IsKeyValueQuoted(queryStringValue));
                    } 

                    WebUtil.CheckSyntaxValid(WebConvert.TryKeyStringToPrimitive(queryStringValue, targetType, out operationParameters[i]));
                }
            } 

            return operationParameters; 
        } 

        /// Removes the parens around the filter part of a query. 
        /// Filter with parens included.
        /// Filter without parens.
        private static string RemoveFilterParens(string filter)
        { 
            Debug.Assert(filter != null, "filter != null");
            WebUtil.CheckSyntaxValid(filter.Length > 0 && filter[0] == '(' && filter[filter.Length - 1] == ')'); 
            return filter.Substring(1, filter.Length - 2); 
        }
 
        /// Project a property with a single element out of the specified query.
        /// Base query to project from.
        /// Property to project.
        /// A query with a composed primitive property projection. 
        private static IQueryable SelectElement(IQueryable query, ResourceProperty property)
        { 
            Debug.Assert(query != null, "query != null"); 
            Debug.Assert(property.Kind != ResourcePropertyKind.ResourceSetReference, "property != ResourcePropertyKind.ResourceSetReference");
 
            ParameterExpression parameter = Expression.Parameter(query.ElementType, "element");
            MemberExpression body = Expression.Property(parameter, property.Name);
            LambdaExpression selector = Expression.Lambda(body, parameter);
 
            MethodInfo method = typeof(RequestUriProcessor).GetMethod("InvokeSelect", BindingFlags.Static | BindingFlags.NonPublic);
            method = method.MakeGenericMethod(query.ElementType, property.Type); 
            return (IQueryable)method.Invoke(null, new object[] { query, selector }); 
        }
 
        /// Project a property with multiple elements out of the specified query.
        /// Base query to project from.
        /// Property to project.
        /// A query with a composed primitive property projection. 
        private static IQueryable SelectMultiple(IQueryable query, ResourceProperty property)
        { 
            Debug.Assert(query != null, "query != null"); 
            Debug.Assert(property.Kind == ResourcePropertyKind.ResourceSetReference, "property == ResourcePropertyKind.ResourceSetReference");
 
            Type enumerableElement = BaseServiceProvider.GetIEnumerableElement(property.Type);
            Debug.Assert(enumerableElement != null, "Providers should never expose a property as a resource-set if it doesn't implement IEnumerable`1.");

            ParameterExpression parameter = Expression.Parameter(query.ElementType, "element"); 
            UnaryExpression body =
                Expression.ConvertChecked( 
                    Expression.Property(parameter, property.Name), 
                    typeof(IEnumerable<>).MakeGenericType(enumerableElement));
            LambdaExpression selector = Expression.Lambda(body, parameter); 

            MethodInfo method = typeof(RequestUriProcessor).GetMethod("InvokeSelectMany", BindingFlags.Static | BindingFlags.NonPublic);
            method = method.MakeGenericMethod(query.ElementType, enumerableElement);
            return (IQueryable)method.Invoke(null, new object[] { query, selector }); 
        }
 
#if ASTORIA_OPEN_OBJECT 

        /// Project a property with a single element out of the specified query over an open property. 
        /// Base query to project from.
        /// Name of property to project.
        /// A query with a composed property projection.
        private static IQueryable SelectOpenProperty(IQueryable query, string propertyName) 
        {
            Debug.Assert(query != null, "query != null"); 
            Debug.Assert(propertyName != null, "propertyName != null"); 

            ParameterExpression parameter = Expression.Parameter(query.ElementType, "element"); 
            MethodCallExpression body = Expression.Call(null /* instance */, LateBoundMethods.GetValueMethodInfo, parameter, Expression.Constant(propertyName));
            LambdaExpression selector = Expression.Lambda(body, parameter);

            MethodInfo method = typeof(RequestUriProcessor).GetMethod("InvokeSelect", BindingFlags.Static | BindingFlags.NonPublic); 
            method = method.MakeGenericMethod(query.ElementType, typeof(object));
            return (IQueryable)method.Invoke(null, new object[] { query, selector }); 
        } 

        /// Project a property with multiple elements out of the specified query over an open property. 
        /// Base query to project from.
        /// Name of property to project.
        /// A query with a composed primitive property projection.
        private static IQueryable SelectOpenPropertyMultiple(IQueryable query, string propertyName) 
        {
            Debug.Assert(query != null, "query != null"); 
            Debug.Assert(propertyName != null, "propertyName != null"); 

            Type enumerableElement = typeof(object); 
            ParameterExpression parameter = Expression.Parameter(query.ElementType, "element");
            Expression body = Expression.Call(null /* instance */, LateBoundMethods.GetSequenceValueMethodInfo, parameter, Expression.Constant(propertyName));
            LambdaExpression selector = Expression.Lambda(body, parameter);
 
            MethodInfo method = typeof(RequestUriProcessor).GetMethod("InvokeSelectMany", BindingFlags.Static | BindingFlags.NonPublic);
            method = method.MakeGenericMethod(query.ElementType, enumerableElement); 
            return (IQueryable)method.Invoke(null, new object[] { query, selector }); 
        }
 
        /// Selects a single resource by key values.
        /// Base query for resources
        /// Key values for the given resource type.
        /// A new query that selects the single resource that matches the specified key values. 
        private static IQueryable SelectOpenResourceByKey(IQueryable query, KeyInstance key)
        { 
            Debug.Assert(query != null, "query != null"); 
            Debug.Assert(query.ElementType == typeof(object), "query.Expression.Type == typeof(object) -- otherwise we aren't primary-key-filtering on an open type.");
            Debug.Assert(key != null && !key.IsEmpty, "key != null && !key.IsEmpty -- otherwise we aren't filtering"); 

            //
            if (key.AreValuesNamed)
            { 
                throw Error.NotImplemented();
            } 
 
            ParameterExpression parameter = Expression.Parameter(typeof(object), "element");
            Expression body = Expression.Call( 
                null /* instance */,
                LateBoundMethods.KeyEqualsMethodInfo,
                Expression.Call(null /* instance */, LateBoundMethods.KeyMethodInfo, parameter),
                Expression.Constant(key.PositionalValues.ToArray(), typeof(object[]))); 
            LambdaExpression predicate = Expression.Lambda(body, parameter);
            return InvokeWhereForType(query, predicate); 
        } 

#endif 

        /// Selects a single resource by key values.
        /// Base query for resources
        /// resource type whose keys are specified 
        /// Key values for the given resource type.
        /// A new query that selects the single resource that matches the specified key values. 
        private static IQueryable SelectResourceByKey(IQueryable query, ResourceType resourceType, KeyInstance key) 
        {
            Debug.Assert(query != null, "query != null"); 
            Debug.Assert(key != null && key.ValueCount != 0, "key != null && key.ValueCount != 0");
            Debug.Assert(resourceType.KeyProperties.Count == key.ValueCount, "resourceType.KeyProperties.Count == key.ValueCount");

            for (int i = 0; i < resourceType.KeyProperties.Count; i++) 
            {
                ResourceProperty keyProperty = resourceType.KeyProperties[i]; 
                Debug.Assert(keyProperty.IsOfKind(ResourcePropertyKind.Key), "keyProperty.IsOfKind(ResourcePropertyKind.Key)"); 

                object keyValue; 
                if (key.AreValuesNamed)
                {
                    keyValue = key.NamedValues[keyProperty.Name];
                } 
                else
                { 
                    keyValue = key.PositionalValues[i]; 
                }
 
                ParameterExpression parameter = Expression.Parameter(query.ElementType, "element");
                BinaryExpression body = Expression.Equal(
                    Expression.Property(parameter, keyProperty.Name),
                    Expression.Constant(keyValue)); 
                LambdaExpression predicate = Expression.Lambda(body, parameter);
                query = InvokeWhereForType(query, predicate); 
            } 

            return query; 
        }

        /// Determines a matching target kind from the specified type.
        ///  of element to get kind for. 
        ///  that supplies type metadata.
        /// An appropriate  for the specified . 
        private static RequestTargetKind TargetKindFromType(Type type, IDataServiceProvider provider) 
        {
            Debug.Assert(type != null, "type != null"); 
            Debug.Assert(provider != null, "provider != null");

            ResourceTypeKind typeKind = provider.GetResourceTypeKind(type);
            switch (typeKind) 
            {
                case ResourceTypeKind.ComplexType: 
                    return RequestTargetKind.ComplexObject; 
                case ResourceTypeKind.EntityType:
                    return RequestTargetKind.Resource; 
                default:
                    Debug.Assert(typeKind == ResourceTypeKind.Primitive, "typeKind == ResourceTypeKind.Primitive");
                    return RequestTargetKind.Primitive;
            } 
        }
    } 
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//  
//      Provides a class capable of processing Astoria request Uris.
//  
// 
// @owner  [....]
//--------------------------------------------------------------------- 

namespace System.Data.Services
{
    #region Namespaces. 

    using System; 
    using System.Collections; 
    using System.Collections.Generic;
#if ASTORIA_OPEN_OBJECT 
    using System.Data.Services.OpenTypes;
#endif
    using System.Data.Services.Parsing;
    using System.Data.Services.Providers; 
    using System.Diagnostics;
    using System.Linq; 
    using System.Linq.Expressions; 
    using System.Reflection;
 
    #endregion Namespaces.

    // Syntax for Astoria segments:
    // segment          ::= identifier [ query ] 
    // query            ::= "(" key ")"
    // key              ::= keyvalue *["," keyvalue] 
    // keyvalue         ::= *quotedvalue | *unquotedvalue 
    // quotedvalue      ::= "'" *qvchar "'"
    // qvchar           ::= char-except-quote | "''" 
    // unquotedvalue    ::= char

    /// 
    /// Use this class to process a web data service request Uri. 
    /// 
    internal static class RequestUriProcessor 
    { 
        /// MethodInfo for the RequestUriProcessor.InvokeWhere method.
        private static readonly MethodInfo InvokeWhereMethodInfo = typeof(RequestUriProcessor).GetMethod("InvokeWhere", BindingFlags.NonPublic | BindingFlags.Static); 

        /// Recursion limit on segment length.
        private const int RecursionLimit = 100;
 
        /// 
        /// Parses the request Uri that the host is exposing and returns 
        /// information about the intended results. 
        /// 
        /// Request uri that needs to get processed. 
        /// Data service for which the request is being processed.
        /// 
        /// An initialized RequestDescription instance describing what the
        /// request is for. 
        /// 
        ///  
        /// A  with status code 404 (Not Found) is returned if an identifier 
        /// in a segment cannot be resolved; 400 (Bad Request) is returned if a syntax
        /// error is found when processing a restriction (parenthesized text) or 
        /// in the query portion.
        /// 
        /// 
        /// Very important: no rights are checked on the last segment of the request. 
        /// 
        internal static RequestDescription ProcessRequestUri(Uri absoluteRequestUri, IDataService service) 
        { 
            Debug.Assert(service != null, "service != null");
            Debug.Assert(absoluteRequestUri != null, "absoluteRequestUri != null"); 
            Debug.Assert(absoluteRequestUri.IsAbsoluteUri, "absoluteRequestUri.IsAbsoluteUri(" + absoluteRequestUri + ")");

            string[] segments = EnumerateSegments(absoluteRequestUri, service.Host.AbsoluteServiceUri);
            if (segments.Length > RecursionLimit) 
            {
                throw DataServiceException.CreateBadRequestError(Strings.RequestUriProcessor_TooManySegments); 
            } 

            SegmentInfo[] segmentInfos = CreateSegments(segments, service); 
            SegmentInfo lastSegment = (segmentInfos.Length == 0) ? null : segmentInfos[segmentInfos.Length - 1];
            RequestTargetKind targetKind = (lastSegment == null) ? RequestTargetKind.ServiceDirectory : lastSegment.TargetKind;

            // Create a ResultDescription from the processed segments. 
            RequestDescription resultDescription;
            Uri resultUri = GetResultUri(service.Host); 
            if (targetKind == RequestTargetKind.Metadata || 
                targetKind == RequestTargetKind.Batch ||
                targetKind == RequestTargetKind.ServiceDirectory) 
            {
                resultDescription = new RequestDescription(targetKind, RequestTargetSource.None, resultUri);
            }
            else if (targetKind == RequestTargetKind.VoidServiceOperation) 
            {
                Debug.Assert(lastSegment != null, "lastSegment != null"); 
                Debug.Assert(lastSegment.TargetSource == RequestTargetSource.ServiceOperation, "targetSource == RequestTargetSource.ServiceOperation"); 
                lastSegment.Operation.Method.Invoke(service, lastSegment.OperationParameters);
                resultDescription = new RequestDescription( 
                    RequestTargetKind.VoidServiceOperation,     // targetKind
                    RequestTargetSource.ServiceOperation,       // targetSource
                    resultUri);                                 // resultUri
            } 
            else
            { 
                Debug.Assert(lastSegment != null, "lastSegment != null"); 
                Debug.Assert(
                    targetKind == RequestTargetKind.ComplexObject || 
#if ASTORIA_OPEN_OBJECT
                    targetKind == RequestTargetKind.OpenProperty ||
                    targetKind == RequestTargetKind.OpenPropertyValue ||
#endif 
                    targetKind == RequestTargetKind.Primitive ||
                    targetKind == RequestTargetKind.PrimitiveValue || 
                    targetKind == RequestTargetKind.Resource, 
                    "Known targetKind " + targetKind);
                RequestTargetSource targetSource = lastSegment.TargetSource; 
                ResourceProperty projectedProperty = lastSegment.ProjectedProperty;
                string containerName =
#if ASTORIA_OPEN_OBJECT
                    (lastSegment.TargetKind != RequestTargetKind.PrimitiveValue && 
                     lastSegment.TargetKind != RequestTargetKind.OpenPropertyValue) ?
#else 
                     lastSegment.TargetKind != RequestTargetKind.PrimitiveValue ? 
#endif
                     lastSegment.Identifier : 
                     segmentInfos[segmentInfos.Length - 2].Identifier;
                bool usesContainerName =
                    (targetSource == RequestTargetSource.Property &&
                     projectedProperty != null && 
                     projectedProperty.Kind != ResourcePropertyKind.ResourceSetReference) ||
                    (targetSource == RequestTargetSource.ServiceOperation && 
                     lastSegment.Operation.ResultKind == ServiceOperationResultKind.QueryWithSingleResult); 
                string mimeType =
                    (targetSource == RequestTargetSource.Property && projectedProperty != null) ? projectedProperty.MimeType : 
                    (targetSource == RequestTargetSource.ServiceOperation) ? lastSegment.Operation.MimeType :
                    null;
                resultDescription = new RequestDescription(
                    segmentInfos, 
                    containerName,
                    usesContainerName, 
                    mimeType, 
                    resultUri);
            } 

            // Process query options ($filter, $orderby, $expand, etc.)
            resultDescription = RequestQueryProcessor.ProcessQuery(service, resultDescription);
 
            return resultDescription;
        } 
 
        /// Appends a segment with the specified escaped .
        /// URI to append to. 
        /// Segment text, already escaped.
        /// A new URI with a new segment escaped.
        internal static Uri AppendEscapedSegment(Uri uri, string text)
        { 
            Debug.Assert(uri != null, "uri != null");
            Debug.Assert(text != null, "text != null"); 
 
            UriBuilder builder = new UriBuilder(uri);
            if (!builder.Path.EndsWith("/", StringComparison.Ordinal)) 
            {
                builder.Path += "/";
            }
 
            builder.Path += text;
            return builder.Uri; 
        } 

        /// Appends a segment with the specified unescaped . 
        /// URI to append to.
        /// Segment text, already escaped.
        /// A new URI with a new segment escaped.
        internal static Uri AppendUnescapedSegment(Uri uri, string text) 
        {
            Debug.Assert(uri != null, "uri != null"); 
            Debug.Assert(text != null, "text != null"); 
            return AppendEscapedSegment(uri, Uri.EscapeDataString(text));
        } 

        /// Gets the absolute URI that a reference (typically from a POST or PUT body) points to.
        /// Textual, URI-encoded reference.
        /// Request parameters with request and service URIs. 
        /// The absolute URI that  resolves to.
        internal static Uri GetAbsoluteUriFromReference(string reference, CachedRequestParams request) 
        { 
            return GetAbsoluteUriFromReference(reference, request.AbsoluteServiceUri);
        } 

        /// Gets the absolute URI that a reference (typically from a POST or PUT body) points to.
        /// Textual, URI-encoded reference.
        /// Absolure URI for service, used to validate that the URI points within. 
        /// The absolute URI that  resolves to.
        internal static Uri GetAbsoluteUriFromReference(string reference, Uri absoluteServiceUri) 
        { 
            Debug.Assert(!String.IsNullOrEmpty(reference), "!String.IsNullOrEmpty(reference) -- caller should check and throw appropriate message");
            Debug.Assert(absoluteServiceUri != null, "absoluteServiceUri != null"); 
            Debug.Assert(absoluteServiceUri.IsAbsoluteUri, "absoluteServiceUri.IsAbsoluteUri(" + absoluteServiceUri + ")");

            Uri referenceAsUri = new Uri(reference, UriKind.RelativeOrAbsolute);
            if (!referenceAsUri.IsAbsoluteUri) 
            {
                string slash = String.Empty; 
                if (absoluteServiceUri.OriginalString.EndsWith("/", StringComparison.Ordinal)) 
                {
                    if (reference.StartsWith("/", StringComparison.Ordinal)) 
                    {
                        reference = reference.Substring(1, reference.Length - 1);
                    }
                } 
                else if (!reference.StartsWith("/", StringComparison.Ordinal))
                { 
                    slash = "/"; 
                }
 
                referenceAsUri = new Uri(absoluteServiceUri.OriginalString + slash + reference);
            }

            if (!UriUtil.UriInvariantInsensitiveIsBaseOf(absoluteServiceUri, referenceAsUri)) 
            {
                string message = Strings.BadRequest_RequestUriDoesNotHaveTheRightBaseUri(referenceAsUri, absoluteServiceUri); 
                throw DataServiceException.CreateBadRequestError(message); 
            }
 
            Debug.Assert(
                referenceAsUri.IsAbsoluteUri,
                "referenceAsUri.IsAbsoluteUri(" + referenceAsUri + ") - otherwise composition from absolute yielded relative");
 
            return referenceAsUri;
        } 
 
        /// Gets the specified  as a string suitable for an HTTP request.
        ///  to get string for. 
        /// A string suitable for an HTTP request.
        internal static string GetHttpRequestUrl(Uri uri)
        {
            Debug.Assert(uri != null, "uri != null"); 
            if (uri.IsAbsoluteUri)
            { 
                return uri.GetComponents(UriComponents.HttpRequestUrl, UriFormat.UriEscaped); 
            }
            else 
            {
                return uri.OriginalString;
            }
        } 

        /// Gets the URI to the results, without the query component. 
        /// Host with request information. 
        /// The URI to the results, without the query component.
        internal static Uri GetResultUri(IDataServiceHost host) 
        {
            Debug.Assert(host != null, "host != null");
            Uri requestUri = GetAbsoluteRequestUri(host);
            UriBuilder resultBuilder = new UriBuilder(requestUri); 
            resultBuilder.Query = null;
 
            // This is fix for bug 565322. 
            // Since we don't allow uri to compose on collections, () must be present
            // as the last thing in the uri, if present. We need to remove the () from 
            // the uri, since its a optional thing and we want to return a canonical
            // uri from the server.
            if (resultBuilder.Path.EndsWith("()", StringComparison.Ordinal))
            { 
                resultBuilder.Path = resultBuilder.Path.Substring(0, resultBuilder.Path.Length - 2);
            } 
 
            return resultBuilder.Uri;
        } 

        /// Gets a non-null  for the hosted service.
        /// Host for the service.
        /// A non-null  for the hosted service. 
        internal static Uri GetAbsoluteRequestUri(IDataServiceHost host)
        { 
            Debug.Assert(host != null, "host != null"); 
            Uri serviceUri = host.AbsoluteRequestUri;
            if (serviceUri == null) 
            {
                throw new InvalidOperationException(Strings.RequestUriProcessor_AbsoluteRequestUriCannotBeNull);
            }
            else if (!serviceUri.IsAbsoluteUri) 
            {
                throw new InvalidOperationException(Strings.RequestUriProcessor_AbsoluteRequestUriMustBeAbsolute); 
            } 
            else
            { 
                return serviceUri;
            }
        }
 
        /// Gets a non-null  for the hosted service.
        /// Host for the service. 
        /// A non-null  for the hosted service. 
        internal static Uri GetServiceUri(IDataServiceHost host)
        { 
            Debug.Assert(host != null, "host != null");
            Uri serviceUri = host.AbsoluteServiceUri;
            if (serviceUri == null)
            { 
                throw new InvalidOperationException(Strings.RequestUriProcessor_AbsoluteServiceUriCannotBeNull);
            } 
            else if (!serviceUri.IsAbsoluteUri) 
            {
                throw new InvalidOperationException(Strings.RequestUriProcessor_AbsoluteServiceUriMustBeAbsolute); 
            }
            else
            {
                return serviceUri; 
            }
        } 
 
        /// 
        /// Given the uri, extract the key values from the uri 
        /// 
        /// uri from which the key values needs to be extracted
        /// Data context for which the request is being processed.
        /// returns the name of the source resource container name 
        /// key values as specified in the uri
        internal static KeyInstance ExtractKeyValuesFromUri(Uri absoluteRequestUri, IDataService service, out string containerName) 
        { 
            Debug.Assert(absoluteRequestUri != null, "absoluteRequestUri != null");
            Debug.Assert(absoluteRequestUri.IsAbsoluteUri, "absoluteRequestUri.IsAbsoluteUri(" + absoluteRequestUri + ")"); 
            Debug.Assert(service != null, "service != null");

            RequestDescription description = RequestUriProcessor.ProcessRequestUri(absoluteRequestUri, service);
 
            //
 
 
            if (description.TargetKind != RequestTargetKind.Resource || !description.IsSingleResult || description.TargetSource != RequestTargetSource.EntitySet)
            { 
                throw DataServiceException.CreateBadRequestError(Strings.BadRequestStream_MustSpecifyCanonicalUriInPayload(absoluteRequestUri));
            }

            // Dereferencing an object by specifying its key values implied the right to read it. 
            Debug.Assert(description.LastSegmentInfo.SingleResult, "description.LastSegmentInfo.SingleResult");
            service.Configuration.CheckResourceRightsForRead(description.LastSegmentInfo.TargetContainer, true /* singleResult */); 
 
            containerName = description.ContainerName;
 
            Debug.Assert(description.LastSegmentInfo.Key != null && !description.LastSegmentInfo.Key.IsEmpty, "Key Must be specified");
            return description.LastSegmentInfo.Key;
        }
 
        /// Invokes Queryable.Select for the specified query and selector.
        /// Query to invoke .Select method on. 
        /// Type that will be projected out. 
        /// Selector lambda expression.
        /// The resulting query. 
        internal static IQueryable InvokeSelectForTypes(IQueryable query, Type projectedType, LambdaExpression selector)
        {
            Debug.Assert(query != null, "query != null");
            Debug.Assert(projectedType != null, "projectedType != null"); 
            Debug.Assert(selector != null, "selector != null");
            MethodInfo method = typeof(RequestUriProcessor).GetMethod("InvokeSelect", BindingFlags.Static | BindingFlags.NonPublic); 
            method = method.MakeGenericMethod(query.ElementType, projectedType); 
            return (IQueryable)method.Invoke(null, new object[] { query, selector });
        } 

        /// Invokes Queryable.Where for the specified query and predicate.
        /// Query to invoke .Where method on.
        /// Predicate to pass as argument. 
        /// The resulting query.
        internal static IQueryable InvokeWhereForType(IQueryable query, LambdaExpression predicate) 
        { 
            Debug.Assert(query != null, "query != null");
            Debug.Assert(predicate != null, "predicate != null"); 
            MethodInfo whereMethod = InvokeWhereMethodInfo.MakeGenericMethod(query.ElementType);
            return (IQueryable)whereMethod.Invoke(null, new object[] { query, predicate });
        }
 
#if ASTORIA_OPEN_OBJECT
 
        ///  
        /// Composes the existing query on an open property segment to include the key.
        ///  
        /// Filter portion of segment.
        /// Target for the composition.
        private static void ComposeOpenPropertyQuery(string filter, SegmentInfo segment)
        { 
            Debug.Assert(filter != null, "filter != null");
            Debug.Assert(segment != null, "segment != null"); 
 
            segment.TargetKind = RequestTargetKind.OpenProperty;
            segment.Key = ExtractOpenPropertyKeyValues(filter); 
            segment.SingleResult = !segment.Key.IsEmpty;
            if (segment.SingleResult)
            {
                segment.RequestQueryable = SelectOpenResourceByKey(segment.RequestQueryable, segment.Key); 
            }
        } 
 
#endif
 
        /// Composes the filter portion of a segment onto the specifies query.
        /// Data provider that supplies metadata.
        /// Filter portion of segment, possibly null.
        /// Segment on which to compose. 
        private static void ComposeQuery(IDataServiceProvider provider, string filter, SegmentInfo segment)
        { 
            Debug.Assert(provider != null, "provider != null"); 
            Debug.Assert(filter != null, "filter != null");
            Debug.Assert(segment != null, "segment!= null"); 
            Debug.Assert(segment.SingleResult == false, "segment.SingleResult == false");

            ResourceType resourceType = provider.GetResourceType(segment.RequestQueryable.ElementType);
            segment.Key = ExtractKeyValues(resourceType, filter); 
            segment.SingleResult = !segment.Key.IsEmpty;
            segment.TargetKind = RequestTargetKind.Resource; 
            if (segment.SingleResult) 
            {
                if (!segment.Key.AreValuesNamed && segment.Key.ValueCount > 1) 
                {
                    throw DataServiceException.CreateBadRequestError(Strings.RequestUriProcessor_KeysMustBeNamed);
                }
 
                segment.RequestQueryable = SelectResourceByKey(segment.RequestQueryable, resourceType, segment.Key);
                segment.SingleResult = true; 
            } 
        }
 
        /// Creates the first  for a request.
        /// Service for which the request is being processed.
        /// Identifier portion of segment.
        /// Whether rights should be checked on this segment. 
        /// Query portion with key; possibly null.
        /// whether this segment references some other segment. 
        /// A description of the information on the segment. 
        private static SegmentInfo CreateFirstSegment(IDataService service, string identifier, bool checkRights, string queryPortion, out bool crossReferencingUrl)
        { 
            Debug.Assert(service != null, "service != null");
            Debug.Assert(identifier != null, "identifier != null");

            crossReferencingUrl = false; 
            SegmentInfo segment = new SegmentInfo();
            segment.Identifier = identifier; 
 
            // Look for well-known system entry points.
            if (segment.Identifier == XmlConstants.UriMetadataSegment) 
            {
                WebUtil.CheckSyntaxValid(queryPortion == null);
                segment.TargetKind = RequestTargetKind.Metadata;
                return segment; 
            }
 
            if (segment.Identifier == XmlConstants.UriBatchSegment) 
            {
                WebUtil.CheckSyntaxValid(queryPortion == null); 
                segment.TargetKind = RequestTargetKind.Batch;
                return segment;
            }
 
            // Look for a service operation.
            segment.Operation = service.Provider.TryResolveServiceOperation(segment.Identifier); 
            if (segment.Operation != null) 
            {
                WebUtil.DebugEnumIsDefined(segment.Operation.ResultKind); 
                segment.TargetSource = RequestTargetSource.ServiceOperation;
                string requiredMethod = segment.Operation.Invoke ? XmlConstants.HttpMethodPost : XmlConstants.HttpMethodGet;
                if (service.Host.RequestHttpMethod != requiredMethod)
                { 
                    throw DataServiceException.CreateMethodNotAllowed(Strings.RequestUriProcessor_MethodNotAllowed, requiredMethod);
                } 
 
                segment.TargetContainer = segment.Operation.EntitySet;
                segment.TargetElementType = segment.Operation.ResultType; 
                segment.OperationParameters = ReadOperationParameters(service.Host, segment.Operation);
                switch (segment.Operation.ResultKind)
                {
                    case ServiceOperationResultKind.QueryWithMultipleResults: 
                    case ServiceOperationResultKind.QueryWithSingleResult:
                        segment.RequestQueryable = (IQueryable)segment.Operation.Method.Invoke(service, segment.OperationParameters); 
                        WebUtil.CheckResourceExists(segment.RequestQueryable != null, segment.Identifier); 
                        segment.SingleResult = (segment.Operation.ResultKind == ServiceOperationResultKind.QueryWithSingleResult);
                        break; 

                    case ServiceOperationResultKind.DirectValue:
                    case ServiceOperationResultKind.Enumeration:
                        object methodResult; 
                        try
                        { 
                            methodResult = segment.Operation.Method.Invoke(service, segment.OperationParameters); 
                        }
                        catch (TargetInvocationException exception) 
                        {
                            ErrorHandler.HandleTargetInvocationException(exception);
                            throw;
                        } 

                        segment.SingleResult = (segment.Operation.ResultKind == ServiceOperationResultKind.DirectValue); 
                        WebUtil.CheckResourceExists(segment.SingleResult || methodResult != null, segment.Identifier);   // Enumerations shouldn't be null. 
                        segment.RequestEnumerable = segment.SingleResult ? new object[1] { methodResult } : (IEnumerable)methodResult;
                        segment.TargetElementType = segment.Operation.ResultType; 
                        segment.TargetKind = TargetKindFromType(segment.TargetElementType, service.Provider);
                        WebUtil.CheckSyntaxValid(queryPortion == null);
                        RequestQueryProcessor.CheckEmptyQueryArguments(service);
                        break; 

                    default: 
                        Debug.Assert(segment.Operation.ResultKind == ServiceOperationResultKind.Nothing, "segment.Operation.ResultKind == ServiceOperationResultKind.Nothing"); 
                        segment.TargetKind = RequestTargetKind.VoidServiceOperation;
                        break; 
                }

                if (segment.RequestQueryable != null)
                { 
                    segment.TargetKind = TargetKindFromType(segment.RequestQueryable.ElementType, service.Provider);
                    if (queryPortion != null) 
                    { 
                        WebUtil.CheckSyntaxValid(!segment.SingleResult);
                        ComposeQuery(service.Provider, queryPortion, segment); 
                    }
                }

                if (checkRights) 
                {
                    service.Configuration.CheckServiceRights(segment.Operation, segment.SingleResult); 
                } 

                return segment; 
            }

            SegmentInfo newSegmentInfo = service.GetSegmentForContentId(segment.Identifier);
            if (newSegmentInfo != null) 
            {
                newSegmentInfo.Identifier = segment.Identifier; 
                crossReferencingUrl = true; 
                return newSegmentInfo;
            } 

            // Look for an entity set.
            ResourceContainer container = service.Provider.TryResolveContainerName(segment.Identifier);
            WebUtil.CheckResourceExists(container != null, segment.Identifier); 
            segment.RequestQueryable = service.Provider.ResolveContainerName(segment.Identifier);
            WebUtil.CheckResourceExists(segment.RequestQueryable != null, segment.Identifier); 
            segment.TargetContainer = container; 
            segment.TargetElementType = container.ElementType;
            segment.TargetSource = RequestTargetSource.EntitySet; 
            segment.TargetKind = RequestTargetKind.Resource;
            segment.SingleResult = false;
            if (queryPortion != null)
            { 
                ComposeQuery(service.Provider, queryPortion, segment);
            } 
 
            if (checkRights)
            { 
                service.Configuration.CheckResourceRightsForRead(container, segment.SingleResult);
            }

            segment.RequestQueryable = DataServiceConfiguration.ComposeResourceContainer(service, container, segment.RequestQueryable); 
            return segment;
        } 
 
        /// Creates a  array for the given .
        /// Segments to process. 
        /// Service for which segments are being processed.
        /// Segment information describing the given .
        private static SegmentInfo[] CreateSegments(string[] segments, IDataService service)
        { 
            Debug.Assert(segments != null, "segments != null");
            Debug.Assert(service != null, "service != null"); 
            SegmentInfo previous = null; 
            SegmentInfo[] segmentInfos = new SegmentInfo[segments.Length];
            bool crossReferencingUri = false; 
            bool postLinkSegment = false;
            for (int i = 0; i < segments.Length; i++)
            {
                string segmentText = segments[i]; 
                bool checkRights = (i != segments.Length - 1);  // Whether rights should be checked on this segment.
                string identifier; 
                bool hasQuery = ExtractSegmentIdentifier(segmentText, out identifier); 
                string queryPortion = hasQuery ? segmentText.Substring(identifier.Length) : null;
                SegmentInfo segment; 

                // We allow a single trailing '/', which results in an empty segment.
                // However System.Uri removes it, so any empty segment we see is a 404 error.
                if (identifier.Length == 0) 
                {
                    throw DataServiceException.ResourceNotFoundError(Strings.RequestUriProcessor_EmptySegmentInRequestUrl); 
                } 

                if (previous == null) 
                {
                    segment = CreateFirstSegment(service, identifier, checkRights, queryPortion, out crossReferencingUri);
                }
                else if (previous.TargetKind == RequestTargetKind.Batch || 
                        previous.TargetKind == RequestTargetKind.Metadata ||
                        previous.TargetKind == RequestTargetKind.PrimitiveValue || 
                        previous.TargetKind == RequestTargetKind.VoidServiceOperation) 
                {
                    // Nothing can come after a $metadata, $value or $batch segment. 
                    // Nothing can come after a service operation with void return type.
                    throw DataServiceException.ResourceNotFoundError(Strings.RequestUriProcessor_MustBeLeafSegment(previous.Identifier));
                }
                else if (postLinkSegment) 
                {
                    throw DataServiceException.ResourceNotFoundError(Strings.RequestUriProcessor_CannotSpecifyAfterPostLinkSegment(identifier, XmlConstants.UriLinkSegment)); 
                } 
                else if (previous.TargetKind == RequestTargetKind.Primitive)
                { 
                    // $value is the well-known identifier to return a primitive property
                    // in its natural MIME format.
                    if (identifier != XmlConstants.UriValueSegment)
                    { 
                        throw DataServiceException.ResourceNotFoundError(Strings.RequestUriProcessor_ValueSegmentAfterScalarPropertySegment(previous.Identifier, identifier));
                    } 
 
                    WebUtil.CheckSyntaxValid(!hasQuery);
                    segment = new SegmentInfo(previous); 
                    segment.Identifier = identifier;
                    segment.SingleResult = true;
                    segment.TargetKind = RequestTargetKind.PrimitiveValue;
                } 
                else if ((
#if ASTORIA_OPEN_OBJECT 
                        previous.TargetKind == RequestTargetKind.OpenProperty || 
#endif
                          previous.TargetKind == RequestTargetKind.Resource) && 
                          previous.SingleResult &&
                          identifier == XmlConstants.UriLinkSegment)
                {
                    segment = new SegmentInfo(previous); 
                    segment.Identifier = identifier;
                    segment.TargetKind = RequestTargetKind.Link; 
                } 
                else
                { 
                    Debug.Assert(
                        previous.TargetKind == RequestTargetKind.ComplexObject ||
                        previous.TargetKind == RequestTargetKind.Resource ||
#if ASTORIA_OPEN_OBJECT 
                        previous.TargetKind == RequestTargetKind.OpenProperty ||
#endif 
                        previous.TargetKind == RequestTargetKind.Link, 
                        "previous.TargetKind(" + previous.TargetKind + ") can have properties");
 
                    postLinkSegment = (previous.TargetKind == RequestTargetKind.Link);

                    // Enumerable results cannot be composed at all.
                    if (previous.Operation != null && 
                        previous.Operation.ResultKind == ServiceOperationResultKind.Enumeration)
                    { 
                        throw DataServiceException.ResourceNotFoundError( 
                            Strings.RequestUriProcessor_IEnumerableServiceOperationsCannotBeFurtherComposed(previous.Identifier));
                    } 

                    if (!previous.SingleResult)
                    {
                        throw DataServiceException.CreateBadRequestError( 
                            Strings.RequestUriProcessor_CannotQueryCollections(previous.Identifier));
                    } 
 
                    segment = new SegmentInfo();
                    segment.Identifier = identifier; 
                    segment.TargetSource = RequestTargetSource.Property;

                    // A segment will correspond to a property in the object model;
                    // if we are processing an open type, anything further in the 
                    // URI also represents an open type property.
#if ASTORIA_OPEN_OBJECT 
                    if (previous.TargetElementType == typeof(object)) 
                    {
                        Debug.Assert(previous.TargetKind == RequestTargetKind.OpenProperty, "For open properties, the target element type must be object"); 
                        segment.ProjectedProperty = null;
                    }
                    else
#endif 
                    {
#if ASTORIA_OPEN_OBJECT 
                        Debug.Assert(previous.TargetKind != RequestTargetKind.OpenProperty, "Since the query element type is known, this can't be open property"); 
#endif
                        segment.ProjectedProperty = service.Provider.TryResolvePropertyName(previous.TargetElementType, identifier); 
#if !ASTORIA_OPEN_OBJECT
                        WebUtil.CheckResourceExists(segment.ProjectedProperty != null, identifier);
#endif
                    } 

#if ASTORIA_OPEN_OBJECT 
                    if (identifier == XmlConstants.UriValueSegment && previous.TargetKind == RequestTargetKind.OpenProperty) 
                    {
                        segment.RequestEnumerable = previous.RequestEnumerable; 
                        segment.SingleResult = true;
                        segment.TargetKind = RequestTargetKind.OpenPropertyValue;
                        segment.TargetElementType = previous.TargetElementType;
                    } 
                    else if (segment.ProjectedProperty == null)
                    { 
                        // Handle an open type property. If the current leaf isn't an 
                        // object (which implies it's already an open type), then
                        // it should be marked as an open type. 
                        if (previous.TargetElementType != typeof(object))
                        {
                            ResourceType resourceType = service.Provider.GetResourceType(previous.TargetElementType);
                            Debug.Assert(resourceType != null, "resourceType != null -- otherwise WebUtil.CheckResourceExists should have thrown."); 
                            WebUtil.CheckResourceExists(resourceType.IsOpenType, segment.Identifier);
                        } 
 
                        segment.TargetElementType = typeof(object);
                        segment.TargetKind = RequestTargetKind.OpenProperty; 
                        segment.SingleResult = !hasQuery;

                        if (!crossReferencingUri)
                        { 
                            if (hasQuery)
                            { 
                                segment.RequestQueryable = SelectOpenPropertyMultiple(previous.RequestQueryable, identifier); 
                                ComposeOpenPropertyQuery(queryPortion, segment);
                            } 
                            else
                            {
                                segment.RequestQueryable = SelectOpenProperty(previous.RequestQueryable, identifier);
                            } 
                        }
                    } 
                    else 
#endif
                    { 
                        // Handle a strongly-typed property.
                        segment.TargetContainer = segment.ProjectedProperty.ResourceContainer;
                        segment.TargetElementType = segment.ProjectedProperty.ResourceClrType;
                        ResourcePropertyKind propertyKind = segment.ProjectedProperty.Kind; 
                        segment.SingleResult = (propertyKind != ResourcePropertyKind.ResourceSetReference);
                        if (!crossReferencingUri) 
                        { 
                            segment.RequestQueryable = segment.SingleResult ?
                                SelectElement(previous.RequestQueryable, segment.ProjectedProperty) : 
                                SelectMultiple(previous.RequestQueryable, segment.ProjectedProperty);
                        }

                        if (previous.TargetKind == RequestTargetKind.Link && 
                            segment.ProjectedProperty.TypeKind != ResourceTypeKind.EntityType)
                        { 
                            throw DataServiceException.CreateBadRequestError(Strings.RequestUriProcessor_LinkSegmentMustBeFollowedByEntitySegment(identifier, XmlConstants.UriLinkSegment)); 
                        }
 
                        switch (propertyKind)
                        {
                            case ResourcePropertyKind.ComplexType:
                                segment.TargetKind = RequestTargetKind.ComplexObject; 
                                break;
                            case ResourcePropertyKind.ResourceReference: 
                            case ResourcePropertyKind.ResourceSetReference: 
                                segment.TargetKind = RequestTargetKind.Resource;
                                segment.TargetContainer = service.Provider.GetContainer( 
                                    previous.TargetContainer.Name, previous.TargetElementType, segment.ProjectedProperty);
                                Debug.Assert(
                                    segment.TargetContainer != null,
                                    "segment.TargetContainer != null -- for strongly typed navigation properties, we should always know the target container"); 
                                break;
                            default: 
                                Debug.Assert(segment.ProjectedProperty.IsOfKind(ResourcePropertyKind.Primitive), "must be primitive type property"); 
                                segment.TargetKind = RequestTargetKind.Primitive;
                                break; 
                        }

                        if (hasQuery)
                        { 
                            WebUtil.CheckSyntaxValid(!segment.SingleResult);
                            if (!crossReferencingUri) 
                            { 
                                ComposeQuery(service.Provider, queryPortion, segment);
                            } 
                            else
                            {
                                throw DataServiceException.CreateBadRequestError(Strings.BadRequest_ResourceCanBeCrossReferencedOnlyForBindOperation);
                            } 
                        }
 
                        // Do security checks and authorization query composition. 
                        if (segment.TargetContainer != null)
                        { 
                            if (checkRights)
                            {
                                service.Configuration.CheckResourceRightsForRead(segment.TargetContainer, segment.SingleResult);
                            } 

                            if (!crossReferencingUri) 
                            { 
                                segment.RequestQueryable = DataServiceConfiguration.ComposeResourceContainer(service, segment.TargetContainer, segment.RequestQueryable);
                            } 
                        }
                    }
                }
 
#if DEBUG
                segment.AssertValid(); 
#endif 
                segmentInfos[i] = segment;
                previous = segment; 
            }

            if (segments.Length != 0 && previous.TargetKind == RequestTargetKind.Link)
            { 
                throw DataServiceException.CreateBadRequestError(Strings.RequestUriProcessor_MissingSegmentAfterLink(XmlConstants.UriLinkSegment));
            } 
 
            return segmentInfos;
        } 

        /// 
        /// Returns an object that can enumerate the segments in the specified path (eg: /foo/bar -> foo, bar).
        ///  
        /// A valid path portion of an uri.
        /// baseUri for the request that is getting processed. 
        /// An enumerable object of unescaped segments. 
        private static string[] EnumerateSegments(Uri absoluteRequestUri, Uri baseUri)
        { 
            Debug.Assert(absoluteRequestUri != null, "absoluteRequestUri != null");
            Debug.Assert(absoluteRequestUri.IsAbsoluteUri, "absoluteRequestUri.IsAbsoluteUri(" + absoluteRequestUri.IsAbsoluteUri + ")");
            Debug.Assert(baseUri != null, "baseUri != null");
            Debug.Assert(baseUri.IsAbsoluteUri, "baseUri.IsAbsoluteUri(" + baseUri + ")"); 

            if (!UriUtil.UriInvariantInsensitiveIsBaseOf(baseUri, absoluteRequestUri)) 
            { 
                throw DataServiceException.CreateBadRequestError(Strings.BadRequest_RequestUriDoesNotHaveTheRightBaseUri(absoluteRequestUri, baseUri));
            } 

            try
            {
                // 
                Uri uri = absoluteRequestUri;
                int numberOfSegmentsToSkip = 0; 
 
                // Since there is a svc part in the segment, we will need to skip 2 segments
                numberOfSegmentsToSkip = baseUri.Segments.Length; 

                string[] uriSegments = uri.Segments;
                int populatedSegmentCount = 0;
                for (int i = numberOfSegmentsToSkip; i < uriSegments.Length; i++) 
                {
                    string segment = uriSegments[i]; 
                    if (segment.Length != 0 && segment != "/") 
                    {
                        populatedSegmentCount++; 
                    }
                }

                string[] segments = new string[populatedSegmentCount]; 
                int segmentIndex = 0;
                for (int i = numberOfSegmentsToSkip; i < uriSegments.Length; i++) 
                { 
                    string segment = uriSegments[i];
                    if (segment.Length != 0 && segment != "/") 
                    {
                        if (segment[segment.Length - 1] == '/')
                        {
                            segment = segment.Substring(0, segment.Length - 1); 
                        }
 
                        segments[segmentIndex++] = Uri.UnescapeDataString(segment); 
                    }
                } 

                Debug.Assert(segmentIndex == segments.Length, "segmentIndex == segments.Length -- otherwise we mis-counted populated/skipped segments.");
                return segments;
            } 
            catch (UriFormatException)
            { 
                throw DataServiceException.CreateSyntaxError(); 
            }
        } 

        /// Returns an object that can enumerate key values.
        /// resource type whose keys need to be extracted
        /// Key (query) part of an Astoria segment. 
        /// An object that can enumerate key values.
        private static KeyInstance ExtractKeyValues(ResourceType resourceType, string filter) 
        { 
            KeyInstance key;
            filter = RemoveFilterParens(filter); 
            WebUtil.CheckSyntaxValid(KeyInstance.TryParseFromUri(filter, out key));

            if (!key.IsEmpty)
            { 
                // Make sure the keys specified in the uri matches with the number of keys in the metadata
                if (resourceType.KeyProperties.Count != key.ValueCount) 
                { 
                    throw DataServiceException.CreateBadRequestError(Strings.BadRequest_KeyCountMismatch(resourceType.FullName));
                } 

                WebUtil.CheckSyntaxValid(key.TryConvertValues(resourceType));
            }
 
            return key;
        } 
 
        /// Returns an object that can enumerate key values for an open property.
        /// Key (query) part of an Astoria segment. 
        /// An object that can enumerate key values.
        private static KeyInstance ExtractOpenPropertyKeyValues(string filter)
        {
            KeyInstance key; 
            filter = RemoveFilterParens(filter);
            WebUtil.CheckSyntaxValid(KeyInstance.TryParseFromUri(filter, out key)); 
            key.RemoveQuotes(); 
            return key;
        } 

        /// Extracts the identifier part of the unescaped Astoria segment.
        /// Unescaped Astoria segment.
        /// On returning, the identifier in the segment. 
        /// true if keys follow the identifier.
        private static bool ExtractSegmentIdentifier(string segment, out string identifier) 
        { 
            Debug.Assert(segment != null, "segment != null");
 
            int filterStart = 0;
            while (filterStart < segment.Length && segment[filterStart] != '(')
            {
                filterStart++; 
            }
 
            identifier = segment.Substring(0, filterStart); 
            return filterStart < segment.Length;
        } 

        /// Generic method to invoke a Select method on an IQueryable source.
        /// Element type of the source.
        /// Result type of the projection. 
        /// Source query.
        /// Lambda expression that turns TSource into TResult. 
        /// A new query that projects TSource into TResult. 
        private static IQueryable InvokeSelect(IQueryable source, LambdaExpression selector)
        { 
            Debug.Assert(source != null, "source != null");
            Debug.Assert(selector != null, "selector != null");

            IQueryable typedSource = (IQueryable)source; 
            Expression> typedSelector = (Expression>)selector;
            return Queryable.Select(typedSource, typedSelector); 
        } 

        /// Generic method to invoke a SelectMany method on an IQueryable source. 
        /// Element type of the source.
        /// Result type of the projection.
        /// Source query.
        /// Lambda expression that turns TSource into IEnumerable<TResult>. 
        /// A new query that projects TSource into IEnumerable<TResult>.
        private static IQueryable InvokeSelectMany(IQueryable source, LambdaExpression selector) 
        { 
            Debug.Assert(source != null, "source != null");
            Debug.Assert(selector != null, "selector != null"); 

            IQueryable typedSource = (IQueryable)source;
            Expression>> typedSelector = (Expression>>)selector;
            return Queryable.SelectMany(typedSource, typedSelector); 
        }
 
        /// Generic method to invoke a Where method on an IQueryable source. 
        /// Element type of the source.
        /// Source query. 
        /// Lambda expression that filters the result of the query.
        /// A new query that filters the query.
        private static IQueryable InvokeWhere(IQueryable query, LambdaExpression predicate)
        { 
            Debug.Assert(query != null, "query != null");
            Debug.Assert(predicate != null, "predicate != null"); 
 
            IQueryable typedQueryable = (IQueryable)query;
            Expression> typedPredicate = (Expression>)predicate; 
            return Queryable.Where(typedQueryable, typedPredicate);
        }

        ///  
        /// Reads the parameters for the specified  from the
        /// . 
        ///  
        /// Host with request information.
        /// Operation with parameters to be read. 
        /// A new object[] with parameter values.
        private static object[] ReadOperationParameters(IDataServiceHost host, ServiceOperation operation)
        {
            Debug.Assert(host != null, "host != null"); 

            object[] operationParameters = new object[operation.Parameters.Length]; 
            for (int i = 0; i < operation.Parameters.Length; i++) 
            {
                Type parameterType = operation.Parameters[i].Type; 
                string queryStringValue = host.GetQueryStringItem(operation.Parameters[i].Name);
                Type underlyingType = Nullable.GetUnderlyingType(parameterType);
                if (String.IsNullOrEmpty(queryStringValue))
                { 
                    WebUtil.CheckSyntaxValid(parameterType.IsClass || underlyingType != null);
                    operationParameters[i] = null; 
                } 
                else
                { 
                    // We choose to be a little more flexible than with keys and
                    // allow surrounding whitespace (which is never significant).
                    // See SQLBUDT #555944.
                    queryStringValue = queryStringValue.Trim(); 
                    Type targetType = underlyingType ?? parameterType;
                    if (WebConvert.IsKeyTypeQuoted(targetType)) 
                    { 
                        WebUtil.CheckSyntaxValid(WebConvert.IsKeyValueQuoted(queryStringValue));
                    } 

                    WebUtil.CheckSyntaxValid(WebConvert.TryKeyStringToPrimitive(queryStringValue, targetType, out operationParameters[i]));
                }
            } 

            return operationParameters; 
        } 

        /// Removes the parens around the filter part of a query. 
        /// Filter with parens included.
        /// Filter without parens.
        private static string RemoveFilterParens(string filter)
        { 
            Debug.Assert(filter != null, "filter != null");
            WebUtil.CheckSyntaxValid(filter.Length > 0 && filter[0] == '(' && filter[filter.Length - 1] == ')'); 
            return filter.Substring(1, filter.Length - 2); 
        }
 
        /// Project a property with a single element out of the specified query.
        /// Base query to project from.
        /// Property to project.
        /// A query with a composed primitive property projection. 
        private static IQueryable SelectElement(IQueryable query, ResourceProperty property)
        { 
            Debug.Assert(query != null, "query != null"); 
            Debug.Assert(property.Kind != ResourcePropertyKind.ResourceSetReference, "property != ResourcePropertyKind.ResourceSetReference");
 
            ParameterExpression parameter = Expression.Parameter(query.ElementType, "element");
            MemberExpression body = Expression.Property(parameter, property.Name);
            LambdaExpression selector = Expression.Lambda(body, parameter);
 
            MethodInfo method = typeof(RequestUriProcessor).GetMethod("InvokeSelect", BindingFlags.Static | BindingFlags.NonPublic);
            method = method.MakeGenericMethod(query.ElementType, property.Type); 
            return (IQueryable)method.Invoke(null, new object[] { query, selector }); 
        }
 
        /// Project a property with multiple elements out of the specified query.
        /// Base query to project from.
        /// Property to project.
        /// A query with a composed primitive property projection. 
        private static IQueryable SelectMultiple(IQueryable query, ResourceProperty property)
        { 
            Debug.Assert(query != null, "query != null"); 
            Debug.Assert(property.Kind == ResourcePropertyKind.ResourceSetReference, "property == ResourcePropertyKind.ResourceSetReference");
 
            Type enumerableElement = BaseServiceProvider.GetIEnumerableElement(property.Type);
            Debug.Assert(enumerableElement != null, "Providers should never expose a property as a resource-set if it doesn't implement IEnumerable`1.");

            ParameterExpression parameter = Expression.Parameter(query.ElementType, "element"); 
            UnaryExpression body =
                Expression.ConvertChecked( 
                    Expression.Property(parameter, property.Name), 
                    typeof(IEnumerable<>).MakeGenericType(enumerableElement));
            LambdaExpression selector = Expression.Lambda(body, parameter); 

            MethodInfo method = typeof(RequestUriProcessor).GetMethod("InvokeSelectMany", BindingFlags.Static | BindingFlags.NonPublic);
            method = method.MakeGenericMethod(query.ElementType, enumerableElement);
            return (IQueryable)method.Invoke(null, new object[] { query, selector }); 
        }
 
#if ASTORIA_OPEN_OBJECT 

        /// Project a property with a single element out of the specified query over an open property. 
        /// Base query to project from.
        /// Name of property to project.
        /// A query with a composed property projection.
        private static IQueryable SelectOpenProperty(IQueryable query, string propertyName) 
        {
            Debug.Assert(query != null, "query != null"); 
            Debug.Assert(propertyName != null, "propertyName != null"); 

            ParameterExpression parameter = Expression.Parameter(query.ElementType, "element"); 
            MethodCallExpression body = Expression.Call(null /* instance */, LateBoundMethods.GetValueMethodInfo, parameter, Expression.Constant(propertyName));
            LambdaExpression selector = Expression.Lambda(body, parameter);

            MethodInfo method = typeof(RequestUriProcessor).GetMethod("InvokeSelect", BindingFlags.Static | BindingFlags.NonPublic); 
            method = method.MakeGenericMethod(query.ElementType, typeof(object));
            return (IQueryable)method.Invoke(null, new object[] { query, selector }); 
        } 

        /// Project a property with multiple elements out of the specified query over an open property. 
        /// Base query to project from.
        /// Name of property to project.
        /// A query with a composed primitive property projection.
        private static IQueryable SelectOpenPropertyMultiple(IQueryable query, string propertyName) 
        {
            Debug.Assert(query != null, "query != null"); 
            Debug.Assert(propertyName != null, "propertyName != null"); 

            Type enumerableElement = typeof(object); 
            ParameterExpression parameter = Expression.Parameter(query.ElementType, "element");
            Expression body = Expression.Call(null /* instance */, LateBoundMethods.GetSequenceValueMethodInfo, parameter, Expression.Constant(propertyName));
            LambdaExpression selector = Expression.Lambda(body, parameter);
 
            MethodInfo method = typeof(RequestUriProcessor).GetMethod("InvokeSelectMany", BindingFlags.Static | BindingFlags.NonPublic);
            method = method.MakeGenericMethod(query.ElementType, enumerableElement); 
            return (IQueryable)method.Invoke(null, new object[] { query, selector }); 
        }
 
        /// Selects a single resource by key values.
        /// Base query for resources
        /// Key values for the given resource type.
        /// A new query that selects the single resource that matches the specified key values. 
        private static IQueryable SelectOpenResourceByKey(IQueryable query, KeyInstance key)
        { 
            Debug.Assert(query != null, "query != null"); 
            Debug.Assert(query.ElementType == typeof(object), "query.Expression.Type == typeof(object) -- otherwise we aren't primary-key-filtering on an open type.");
            Debug.Assert(key != null && !key.IsEmpty, "key != null && !key.IsEmpty -- otherwise we aren't filtering"); 

            //
            if (key.AreValuesNamed)
            { 
                throw Error.NotImplemented();
            } 
 
            ParameterExpression parameter = Expression.Parameter(typeof(object), "element");
            Expression body = Expression.Call( 
                null /* instance */,
                LateBoundMethods.KeyEqualsMethodInfo,
                Expression.Call(null /* instance */, LateBoundMethods.KeyMethodInfo, parameter),
                Expression.Constant(key.PositionalValues.ToArray(), typeof(object[]))); 
            LambdaExpression predicate = Expression.Lambda(body, parameter);
            return InvokeWhereForType(query, predicate); 
        } 

#endif 

        /// Selects a single resource by key values.
        /// Base query for resources
        /// resource type whose keys are specified 
        /// Key values for the given resource type.
        /// A new query that selects the single resource that matches the specified key values. 
        private static IQueryable SelectResourceByKey(IQueryable query, ResourceType resourceType, KeyInstance key) 
        {
            Debug.Assert(query != null, "query != null"); 
            Debug.Assert(key != null && key.ValueCount != 0, "key != null && key.ValueCount != 0");
            Debug.Assert(resourceType.KeyProperties.Count == key.ValueCount, "resourceType.KeyProperties.Count == key.ValueCount");

            for (int i = 0; i < resourceType.KeyProperties.Count; i++) 
            {
                ResourceProperty keyProperty = resourceType.KeyProperties[i]; 
                Debug.Assert(keyProperty.IsOfKind(ResourcePropertyKind.Key), "keyProperty.IsOfKind(ResourcePropertyKind.Key)"); 

                object keyValue; 
                if (key.AreValuesNamed)
                {
                    keyValue = key.NamedValues[keyProperty.Name];
                } 
                else
                { 
                    keyValue = key.PositionalValues[i]; 
                }
 
                ParameterExpression parameter = Expression.Parameter(query.ElementType, "element");
                BinaryExpression body = Expression.Equal(
                    Expression.Property(parameter, keyProperty.Name),
                    Expression.Constant(keyValue)); 
                LambdaExpression predicate = Expression.Lambda(body, parameter);
                query = InvokeWhereForType(query, predicate); 
            } 

            return query; 
        }

        /// Determines a matching target kind from the specified type.
        ///  of element to get kind for. 
        ///  that supplies type metadata.
        /// An appropriate  for the specified . 
        private static RequestTargetKind TargetKindFromType(Type type, IDataServiceProvider provider) 
        {
            Debug.Assert(type != null, "type != null"); 
            Debug.Assert(provider != null, "provider != null");

            ResourceTypeKind typeKind = provider.GetResourceTypeKind(type);
            switch (typeKind) 
            {
                case ResourceTypeKind.ComplexType: 
                    return RequestTargetKind.ComplexObject; 
                case ResourceTypeKind.EntityType:
                    return RequestTargetKind.Resource; 
                default:
                    Debug.Assert(typeKind == ResourceTypeKind.Primitive, "typeKind == ResourceTypeKind.Primitive");
                    return RequestTargetKind.Primitive;
            } 
        }
    } 
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK