RequestDescription.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / DataWeb / Server / System / Data / Services / RequestDescription.cs / 1305376 / RequestDescription.cs

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//  
//      Provides a description of the request a client has submitted.
//  
// 
// @owner  [....]
//--------------------------------------------------------------------- 

namespace System.Data.Services
{
    #region Namespaces. 

    using System; 
    using System.Collections; 
    using System.Collections.Generic;
    using System.Data.Services.Providers; 
    using System.Diagnostics;
    using System.Linq;

    #endregion Namespaces. 

    ///  
    /// Query Counting Option 
    /// 
    internal enum RequestQueryCountOption 
    {
        /// Do not count the result set
        None,
 
        /// Count and return value inline (together with data)
        Inline, 
 
        /// Count and return value only (as integer)
        ValueOnly 
    }

    /// 
    /// Use this class to describe the data request a client has 
    /// submitted to the service.
    ///  
    [DebuggerDisplay("RequestDescription={TargetSource} '{ContainerName}' -> {TargetKind} '{TargetResourceType}'")] 
    internal class RequestDescription
    { 
        /// 
        /// The default response version of the data service. If no version is set for a particular response
        /// The DataService will respond with this version (1.0)
        ///  
        internal static readonly Version DataServiceDefaultResponseVersion = new Version(1, 0);
 
        #region Private fields. 

        ///  
        /// Default set of known data service versions, currently 1.0 and 2.0
        /// 
        private static readonly Version[] KnownDataServiceVersions = new Version[] { new Version(1, 0), new Version(2, 0) };
 
        /// The name of the container for results.
        private readonly string containerName; 
 
        /// Root of the projection and expansion tree.
        /// If this is null - no projections or expansions were part of the request. 
        private readonly RootProjectionNode rootProjectionNode;

        /// The MIME type for the requested resource, if specified.
        private readonly string mimeType; 

        /// URI for the result (without the query component). 
        private readonly Uri resultUri; 

        /// SegmentInfo containing information about every segment in the uri 
        private readonly SegmentInfo[] segmentInfos;

        /// Whether the container name should be used to name the result.
        private readonly bool usesContainerName; 

        /// Query count option, whether to count the result set or not, and how 
        private RequestQueryCountOption countOption; 

        /// The value of the row count 
        private long countValue;

        /// The minimum client version requirement
        private Version requireMinimumVersion; 

        /// The server response version 
        private Version responseVersion; 

        ///  
        /// Max version of features used in the request. We need to distinguish between feature versions and response version since
        /// some new features (e.g. Server Projections) do not cause response version to be raised.
        /// 
        private Version maxFeatureVersion; 

        #endregion Private fields. 
 
        /// 
        /// Initializes a new RequestDescription for a query specified by the 
        /// request Uri.
        /// 
        /// The kind of target for the request.
        /// The source for this target. 
        /// URI to the results requested (with no query component).
        internal RequestDescription(RequestTargetKind targetKind, RequestTargetSource targetSource, Uri resultUri) 
        { 
            WebUtil.DebugEnumIsDefined(targetKind);
            Debug.Assert(resultUri != null, "resultUri != null"); 
            Debug.Assert(resultUri.IsAbsoluteUri, "resultUri.IsAbsoluteUri(" + resultUri + ")");

            SegmentInfo segment = new SegmentInfo();
            segment.TargetKind = targetKind; 
            segment.TargetSource = targetSource;
            segment.SingleResult = true; 
            this.segmentInfos = new SegmentInfo[] { segment }; 
            this.resultUri = resultUri;
 
            this.requireMinimumVersion = new Version(1, 0);
            this.responseVersion = DataServiceDefaultResponseVersion;
            this.maxFeatureVersion = new Version(1, 0);
        } 

        ///  
        /// Initializes a new RequestDescription for a query specified by the 
        /// request Uri.
        ///  
        /// list containing information about each segment of the request uri
        /// Name of the container source.
        /// Whether the container name should be used to name the result.
        /// The MIME type for the requested resource, if specified. 
        /// URI to the results requested (with no query component).
        internal RequestDescription( 
            SegmentInfo[] segmentInfos, 
            string containerName,
            bool usesContainerName, 
            string mimeType,
            Uri resultUri)
        {
            Debug.Assert(segmentInfos != null && segmentInfos.Length != 0, "segmentInfos != null && segmentInfos.Length != 0"); 
            Debug.Assert(resultUri != null, "resultUri != null");
            Debug.Assert(resultUri.IsAbsoluteUri, "resultUri.IsAbsoluteUri(" + resultUri + ")"); 
            this.segmentInfos = segmentInfos; 
            this.containerName = containerName;
            this.usesContainerName = usesContainerName; 
            this.mimeType = mimeType;
            this.resultUri = resultUri;

            this.requireMinimumVersion = new Version(1, 0); 
            this.responseVersion = DataServiceDefaultResponseVersion;
            this.maxFeatureVersion = new Version(1, 0); 
        } 

        /// Initializes a new RequestDescription based on an existing one. 
        /// Other description to base new description on.
        /// Query results for new request description.
        /// Projection segment describing the projections on the top level of the query.
        internal RequestDescription( 
            RequestDescription other,
            IEnumerable queryResults, 
            RootProjectionNode rootProjectionNode) 
        {
            Debug.Assert( 
                queryResults == null || other.SegmentInfos != null,
                "queryResults == null || other.SegmentInfos != null -- otherwise there isn't a segment in which to replace the query.");
            Debug.Assert(
                rootProjectionNode == null || queryResults != null, 
                "rootProjectionNode == null || queryResults != null -- otherwise there isn't a query to execute and expand");
 
            this.containerName = other.containerName; 
            this.mimeType = other.mimeType;
            this.usesContainerName = other.usesContainerName; 
            this.resultUri = other.resultUri;
            this.segmentInfos = other.SegmentInfos;
            this.rootProjectionNode = rootProjectionNode;
            this.countOption = other.countOption; 
            this.SkipTokenExpressionCount = other.SkipTokenExpressionCount;
            this.SkipTokenProperties = other.SkipTokenProperties; 
            this.countValue = other.countValue; 

            this.requireMinimumVersion = other.requireMinimumVersion; 
            this.responseVersion = other.responseVersion;
            this.maxFeatureVersion = other.maxFeatureVersion;

            if (queryResults == null) 
            {
                this.segmentInfos = other.SegmentInfos; 
            } 
            else
            { 
                int lastSegmentIndex = other.SegmentInfos.Length - 1;
                SegmentInfo lastSegmentInfo = other.SegmentInfos[lastSegmentIndex];
                lastSegmentInfo.RequestEnumerable = queryResults;
            } 
        }
 
        /// The name of the container for results. 
        internal string ContainerName
        { 
            [DebuggerStepThrough]
            get { return this.containerName; }
        }
 
        /// Root of the projection and expansion tree.
        internal RootProjectionNode RootProjectionNode 
        { 
            [DebuggerStepThrough]
            get { return this.rootProjectionNode; } 
        }

        /// URI for the result (without the query component).
        internal Uri ResultUri 
        {
            [DebuggerStepThrough] 
            get { return this.resultUri; } 
        }
 
        /// Returns the list containing the information about each segment that make up the request uri
        internal SegmentInfo[] SegmentInfos
        {
            [DebuggerStepThrough] 
            get { return this.segmentInfos; }
        } 
 
        /// The base query for the request, before client-specified composition.
        internal IEnumerable RequestEnumerable 
        {
            get { return this.LastSegmentInfo.RequestEnumerable; }
        }
 
        /// Whether the result of this request is a single element.
        internal bool IsSingleResult 
        { 
            get { return this.LastSegmentInfo.SingleResult; }
        } 

        /// The MIME type for the requested resource, if specified.
        internal string MimeType
        { 
            [DebuggerStepThrough]
            get { return this.mimeType; } 
        } 

        /// The kind of target being requested. 
        internal RequestTargetKind TargetKind
        {
            get { return this.LastSegmentInfo.TargetKind; }
        } 

        /// The type of resource targetted by this request. 
        internal ResourceType TargetResourceType 
        {
            get { return this.LastSegmentInfo.TargetResourceType; } 
        }

        /// The type of source for the request target.
        internal RequestTargetSource TargetSource 
        {
            get { return this.LastSegmentInfo.TargetSource; } 
        } 

        ///  
        /// Returns the resource property on which this query is targeted
        /// 
        internal ResourceProperty Property
        { 
            get { return this.LastSegmentInfo.ProjectedProperty; }
        } 
 
        /// Whether the container name should be used to name the result.
        internal bool UsesContainerName 
        {
            [DebuggerStepThrough]
            get { return this.usesContainerName; }
        } 

        /// Returns the last segment 
        internal SegmentInfo LastSegmentInfo 
        {
            get { return this.segmentInfos[this.segmentInfos.Length - 1]; } 
        }

        /// Returns true if the request description refers to a link uri. Otherwise returns false.
        internal bool LinkUri 
        {
            get 
            { 
                return (this.segmentInfos.Length >= 3 && this.segmentInfos[this.segmentInfos.Length - 2].TargetKind == RequestTargetKind.Link);
            } 
        }

        /// Returns the request's counting options
        internal RequestQueryCountOption CountOption 
        {
            get { return this.countOption; } 
            set { this.countOption = value; } 
        }
 
        /// Number of expressions in the $skiptoken for top level expression
        internal int SkipTokenExpressionCount
        {
            get; 
            set;
        } 
 
        /// Collection of properties in the $skiptoken for top level expression
        internal ICollection SkipTokenProperties 
        {
            get;
            set;
        } 

        /// Returns the value of the row count 
        internal long CountValue 
        {
            get { return this.countValue; } 
            set { this.countValue = value; }
        }

        /// The minimum client version requirement 
        internal Version RequireMinimumVersion
        { 
            get { return this.requireMinimumVersion; } 
        }
 
        /// The server response version
        internal Version ResponseVersion
        {
            get { return this.responseVersion; } 
        }
 
        /// Max version of features used in the user's request 
        internal Version MaxFeatureVersion
        { 
            get { return this.maxFeatureVersion; }
        }

        ///  
        /// Is the request for an IEnumerable<T> returning service operation.
        ///  
        internal bool IsRequestForEnumServiceOperation 
        {
            get 
            {
                return this.TargetSource == RequestTargetSource.ServiceOperation && this.SegmentInfos[0].Operation.ResultKind == ServiceOperationResultKind.Enumeration;
            }
        } 

        ///  
        /// Get the single result from the given segment info 
        /// 
        /// segmentInfo which contains the request query 
        /// query result as returned by the IQueryable query
        internal static IEnumerator GetSingleResultFromEnumerable(SegmentInfo segmentInfo)
        {
            IEnumerator queryResults = WebUtil.GetRequestEnumerator(segmentInfo.RequestEnumerable); 
            bool shouldDispose = true;
            try 
            { 
                WebUtil.CheckResourceExists(queryResults.MoveNext(), segmentInfo.Identifier);
 
                CheckQueryResult(queryResults.Current, segmentInfo);

                shouldDispose = false;
                return queryResults; 
            }
            finally 
            { 
                // Dispose the Enumerator in case of error
                if (shouldDispose) 
                {
                    WebUtil.Dispose(queryResults);
                }
            } 
        }
 
        ///  
        /// Checks query result.
        ///  
        /// Query result to be checked.
        /// Segment details for the .
        internal static void CheckQueryResult(object result, SegmentInfo segmentInfo)
        { 
            // e.g. /Customers(4) - if there is a direct reference to an entity, it should not be null.
            // e.g. $value also must not be null, since you are dereferencing the values 
            // Any other case, having null is fine 
            if (segmentInfo.IsDirectReference && result == null)
            { 
                throw DataServiceException.CreateResourceNotFound(segmentInfo.Identifier);
            }

            IEnumerable enumerable; 
            if (segmentInfo.TargetKind == RequestTargetKind.OpenProperty &&
                WebUtil.IsElementIEnumerable(result, out enumerable)) 
            { 
                throw DataServiceException.CreateSyntaxError(
                    Strings.InvalidUri_OpenPropertiesCannotBeCollection(segmentInfo.Identifier)); 
            }
        }

        ///  
        /// Create a new request description from the given request description and new entity as the result.
        ///  
        /// Existing request description. 
        /// entity that needs to be the result of the new request.
        /// container to which the entity belongs to. 
        /// a new instance of request description containing information about the given entity.
        internal static RequestDescription CreateSingleResultRequestDescription(
            RequestDescription description, object entity, ResourceSetWrapper container)
        { 
            // Create a new request description for the results that will be returned.
            SegmentInfo segmentInfo = new SegmentInfo(); 
            segmentInfo.RequestEnumerable = new object[] { entity }; 
            segmentInfo.TargetKind = description.TargetKind;
            segmentInfo.TargetSource = description.TargetSource; 
            segmentInfo.SingleResult = true;
            segmentInfo.ProjectedProperty = description.Property;
            segmentInfo.TargetResourceType = container != null ? container.ResourceType : null;
            segmentInfo.TargetContainer = container; 
            segmentInfo.Identifier = description.LastSegmentInfo.Identifier;
#if DEBUG 
            segmentInfo.AssertValid(); 
#endif
            SegmentInfo[] segmentInfos = description.SegmentInfos; 
            segmentInfos[segmentInfos.Length - 1] = segmentInfo;

            RequestDescription resultDescription = new RequestDescription(
                segmentInfos, 
                container != null ? container.Name : null,
                description.UsesContainerName, 
                description.MimeType, 
                description.ResultUri);
 
            resultDescription.requireMinimumVersion = description.RequireMinimumVersion;
            resultDescription.responseVersion = description.ResponseVersion;
            resultDescription.maxFeatureVersion = description.MaxFeatureVersion;
            return resultDescription; 
        }
 
        ///  
        /// Checks whether etag headers are allowed (both request and response) for this request.
        /// ETag request headers are mainly If-Match and If-None-Match headers 
        /// ETag response header is written only when its valid to specify one of the above mentioned request headers.
        /// 
        /// description about the request uri.
        /// true if If-Match or If-None-Match are allowed request headers for this request, otherwise false. 
        internal static bool IsETagHeaderAllowed(RequestDescription description)
        { 
            // IfMatch and IfNone match request headers are allowed and etag response header must be written 
            // only when the request targets a single resource, which does not have $count and $links segment and there are no $expands query option specified.
            return description.IsSingleResult && description.CountOption != RequestQueryCountOption.ValueOnly && (description.RootProjectionNode == null || !description.RootProjectionNode.ExpansionsSpecified) && !description.LinkUri; 
        }

        /// 
        /// Verify that the request version is a version we know. 
        /// 
        /// request version from the header 
        /// returns true if the request version is known 
        internal static bool IsKnownRequestVersion(Version requestVersion)
        { 
            return KnownDataServiceVersions.Contains(requestVersion);
        }

        ///  
        /// Raise the feature version if the target container contains any type with FF mapped properties that is KeepInContent=false.
        ///  
        /// service instance 
        /// This RequestDescription instance
        internal RequestDescription UpdateAndCheckEpmFeatureVersion(IDataService service) 
        {
            Debug.Assert(this.LastSegmentInfo != null, "this.LastSegmentInfo != null");

            if (this.LinkUri) 
            {
                // For $link operations we raise the feature version if either side of $link points to a set that 
                // is not V1 compatible. 
                ResourceSetWrapper leftSet;
                ResourceSetWrapper rightSet; 
                this.GetLinkedResourceSets(out leftSet, out rightSet);

                Debug.Assert(leftSet != null, "leftSet != null");
                Debug.Assert(rightSet != null, "rightSet != null"); 
                this.UpdateAndCheckEpmFeatureVersion(leftSet, service);
                this.UpdateAndCheckEpmFeatureVersion(rightSet, service); 
            } 
            else
            { 
                // The last segment might not be a resource.  Trace backward to find the target entity resource.
                int resourceIndex = this.GetIndexOfTargetEntityResource();

                // Not every request has a target resource.  Service Operations for example can return just primitives. 
                if (resourceIndex != -1)
                { 
                    ResourceSetWrapper resourceSet = this.SegmentInfos[resourceIndex].TargetContainer; 
                    Debug.Assert(resourceSet != null, "resourceSet != null");
                    this.UpdateAndCheckEpmFeatureVersion(resourceSet, service); 
                }
            }

            return this; 
        }
 
        ///  
        /// Raise the feature version if the given set contains any type with FF mapped properties that is KeepInContent=false.
        ///  
        /// Resource set to test
        /// service instance
        /// This RequestDescription instance
        internal RequestDescription UpdateAndCheckEpmFeatureVersion(ResourceSetWrapper resourceSet, IDataService service) 
        {
            Debug.Assert(resourceSet != null, "resourceSet != null"); 
            Debug.Assert(service != null, "provider != null"); 

            // For feature version we only look at the SET, and bump the version if it's not V1 compatible.  We do this even if the 
            // target kind is not Resource.  So we could have request and response DSV be 1.0 while feature version be 2.0.
            if (!resourceSet.EpmIsV1Compatible(service.Provider))
            {
                this.RaiseFeatureVersion(2, 0, service.Configuration); 
            }
 
            return this; 
        }
 
        /// Updates the response version based on response format and the target resource type
        /// text for Accepts header content required to infere response format
        /// data service provider instance
        /// The instance for which the update of response version happens 
        internal RequestDescription UpdateEpmResponseVersion(string acceptTypesText, DataServiceProviderWrapper provider)
        { 
            return this.UpdateEpmResponseVersion(acceptTypesText, this.LastSegmentInfo.TargetContainer, provider); 
        }
 
        /// Updates the response version based on response format and given resource type
        /// text for Accepts header content required to infere response format
        /// resourceSet to check for friendly feeds presence
        /// data service provider instance 
        /// The instance for which the update of response version happens
        internal RequestDescription UpdateEpmResponseVersion(string acceptTypesText, ResourceSetWrapper resourceSet, DataServiceProviderWrapper provider) 
        { 
            Debug.Assert(provider != null, "provider != null");
 
            // Response will be 2.0 if any of the property in the types contained in the resource set has KeepInContent false
            if (this.TargetKind == RequestTargetKind.Resource)
            {
                Debug.Assert(resourceSet != null, "Must have valid resource set"); 
                if (!resourceSet.EpmIsV1Compatible(provider) && !this.LinkUri)
                { 
                    // Friendly feeds must only bump the response version for Atom responses 
                    if (WebUtil.IsAtomMimeType(acceptTypesText))
                    { 
                        this.RaiseResponseVersion(2, 0);
                    }
                }
            } 

            return this; 
        } 

        ///  
        /// Returns the last segment info whose target request kind is resource
        /// 
        /// The index of the parent resource
        internal int GetIndexOfTargetEntityResource() 
        {
            Debug.Assert(this.segmentInfos.Length >= 1, "this.segmentInfos.Length >= 1"); 
            int result = -1; 
            if (this.LinkUri || this.CountOption == RequestQueryCountOption.ValueOnly)
            { 
                return this.SegmentInfos.Length - 1;
            }

            for (int j = this.SegmentInfos.Length - 1; j >= 0; j--) 
            {
                if (this.segmentInfos[j].TargetKind == RequestTargetKind.Resource || this.segmentInfos[j].HasKeyValues) 
                { 
                    result = j;
                    break; 
                }
            }

            return result; 
        }
 
        ///  
        /// Raise the minimum client version requirement for this request
        ///  
        /// The major segment of the version
        /// The minor segment of the version
        internal void RaiseMinimumVersionRequirement(int major, int minor)
        { 
            this.requireMinimumVersion = RaiseVersion(this.requireMinimumVersion, major, minor);
 
            // Each time we bump the request version we need to bump the max feature version as well. 
            // Note that sometimes we need to bump max feature version even if request version is not raised.
            this.maxFeatureVersion = RaiseVersion(this.maxFeatureVersion, major, minor); 
        }

        /// 
        /// Raise the response version for this request 
        /// 
        /// The major segment of the version 
        /// The minor segment of the version 
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811", Justification = "Will be used for other 2.0 server features")]
        internal void RaiseResponseVersion(int major, int minor) 
        {
            this.responseVersion = RaiseVersion(this.responseVersion, major, minor);

            // Each time we bump the response version we need to bump the max feature version as well. 
            // Note that sometimes we need to bump max feature version even if response version is not raised.
            this.maxFeatureVersion = RaiseVersion(this.maxFeatureVersion, major, minor); 
        } 

        ///  
        /// Raise the version for features used in the user's request
        /// 
        /// The major segment of the version
        /// The minor segment of the version 
        /// Data service configuration instance to validate the feature version.
        internal void RaiseFeatureVersion(int major, int minor, DataServiceConfiguration config) 
        { 
            this.maxFeatureVersion = RaiseVersion(this.maxFeatureVersion, major, minor);
            config.ValidateMaxProtocolVersion(this); 
        }

        /// 
        /// If necessary raises version to the version requested by the user. 
        /// 
        /// Version to raise. 
        /// The major segment of the new version 
        /// The minor segment of the new version
        /// New version if the requested version is greater than the existing version. 
        private static Version RaiseVersion(Version versionToRaise, int major, int minor)
        {
            if (major > versionToRaise.Major ||
                (major == versionToRaise.Major && minor > versionToRaise.Minor)) 
            {
                versionToRaise = new Version(major, minor); 
            } 

            return versionToRaise; 
        }

        /// 
        /// Returns the resource sets on the left and right hand sides of $link. 
        /// 
        /// Resource set to the left of $link. 
        /// Resource set to the right of $link. 
        private void GetLinkedResourceSets(out ResourceSetWrapper leftSet, out ResourceSetWrapper rightSet)
        { 
            Debug.Assert(this.LinkUri, "GetLinkedResourceSets should only be called if this is a $link request.");

            int idx = 0;
            for (; idx < this.segmentInfos.Length; idx++) 
            {
                if (this.segmentInfos[idx].TargetKind == RequestTargetKind.Link) 
                { 
                    break;
                } 
            }

            Debug.Assert(idx > 0 && idx < this.segmentInfos.Length - 1, "idx > 0 && idx < this.segmentInfos.Length - 1");
            leftSet = this.segmentInfos[idx - 1].TargetContainer; 
            rightSet = this.segmentInfos[idx + 1].TargetContainer;
        } 
    } 
}

// 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