Serializer.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 / Serializers / Serializer.cs / 3 / Serializer.cs

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//  
//      An abstract base serializer for various serializers
//  
// 
// @owner  [....]
//--------------------------------------------------------------------- 

namespace System.Data.Services.Serializers
{
    #region Namespaces. 

    using System; 
    using System.Collections; 
    using System.Collections.Generic;
    using System.Data.Services.Providers; 
    using System.Diagnostics;
    using System.IO;
    using System.Text;
 
    #endregion Namespaces.
 
    /// Abstract base class for all serializers. 
    internal abstract class Serializer : IExceptionWriter
    { 
        #region Private fields.

        /// Base URI from which resources should be resolved.
        private readonly Uri absoluteServiceUri; 

        /// Configuration for the service. 
        private readonly DataServiceConfiguration configuration; 

        /// Data provider from which metadata should be gathered. 
        private readonly string httpETagHeaderValue;

        /// Data provider from which metadata should be gathered.
        private readonly IDataServiceProvider provider; 

        /// Description for the requested results. 
        private readonly RequestDescription requestDescription; 

        /// Collection of complex types, used for cycle detection. 
        private HashSet complexTypeCollection;

        /// Resolved segment containers.
        private List segmentContainers; 

        /// Segment names. 
        private List segmentNames; 

        /// Result counts for segments. 
        private List segmentResultCounts;

        #endregion Private fields.
 
        /// Initializes a new base Serializer, ready to write out a description.
        /// Description for the requested results. 
        /// Base URI from which resources should be resolved. 
        /// Service with configuration and provider from which metadata should be gathered.
        /// HTTP ETag header value. 
        internal Serializer(RequestDescription requestDescription, Uri absoluteServiceUri, IDataService service, string httpETagHeaderValue)
        {
            Debug.Assert(requestDescription != null, "requestDescription != null");
            Debug.Assert(absoluteServiceUri != null, "absoluteServiceUri != null"); 
            Debug.Assert(service != null, "service != null");
 
            this.requestDescription = requestDescription; 
            this.absoluteServiceUri = absoluteServiceUri;
            this.configuration = service.Configuration; 
            this.provider = service.Provider;
            this.httpETagHeaderValue = httpETagHeaderValue;
        }
 
        /// Container for the resource being serialized (possibly null).
        protected ResourceContainer CurrentContainer 
        { 
            get
            { 
                if (this.segmentContainers == null || this.segmentContainers.Count == 0)
                {
                    return this.requestDescription.LastSegmentInfo.TargetContainer;
                } 
                else
                { 
                    return this.segmentContainers[this.segmentContainers.Count - 1]; 
                }
            } 
        }

        /// 
        /// Gets the Data provider from which metadata should be gathered. 
        /// 
        protected IDataServiceProvider Provider 
        { 
            [DebuggerStepThrough]
            get { return this.provider; } 
        }

        /// Gets the absolute URI to the service.
        protected Uri AbsoluteServiceUri 
        {
            [DebuggerStepThrough] 
            get { return this.absoluteServiceUri; } 
        }
 
        /// 
        /// Gets the RequestDescription for the request that is getting serialized.
        /// 
        protected RequestDescription RequestDescription 
        {
            [DebuggerStepThrough] 
            get 
            {
                return this.requestDescription; 
            }
        }

        /// Serializes exception information. 
        /// Description of exception to serialize.
        public abstract void WriteException(HandleExceptionArgs args); 
 
        /// 
        /// Gets the uri given the list of resource properties. This logic must be the same across all 
        /// serializers. Hence putting this in a util class
        /// 
        /// instance of the resource type whose properties needs to be returned
        /// Provider from which resource was obtained. 
        /// Container for the resource.
        /// Base URI from which resources should be resolved. 
        /// uri for the given resource 
        internal static Uri GetUri(object resource, IDataServiceProvider provider, ResourceContainer container, Uri absoluteServiceUri)
        { 
            string objectKey = GetObjectKey(resource, provider, container.Name);
            return RequestUriProcessor.AppendEscapedSegment(absoluteServiceUri, objectKey);
        }
 
        /// 
        /// Appends the given entry to the given uri 
        ///  
        /// uri to which the entry needs to be appended
        /// entry which gets appended to the given uri 
        /// new uri with the entry appended to the given uri
        internal static Uri AppendEntryToUri(Uri currentUri, string entry)
        {
            return RequestUriProcessor.AppendUnescapedSegment(currentUri, entry); 
        }
 
        ///  
        /// Handles the complete serialization for the specified .
        ///  
        /// Query results to enumerate.
        /// Whether  was succesfully advanced to the first element.
        /// 
        ///  should correspond to the RequestQuery of the 
        /// RequestDescription object passed while constructing this serializer
        /// We allow the results to be passed in 
        /// to let the query be executed earlier than at result-writing time, which 
        /// helps detect data and query errors where they can be better handled.
        ///  
        internal void WriteRequest(IEnumerator queryResults, bool hasMoved)
        {
            IEnumerable requestEnumerable = this.requestDescription.RequestEnumerable;
            Debug.Assert(requestEnumerable != null, "requestQuery != null"); 
            Debug.Assert(queryResults != null, "queryResults != null");
            IExpandedResult expanded = queryResults as IExpandedResult; 
 
            if (this.requestDescription.LinkUri)
            { 
                this.PushSegmentForRoot();
                if (this.requestDescription.IsSingleResult)
                {
                    this.WriteLink(queryResults.Current); 
                    if (queryResults.MoveNext())
                    { 
                        throw new InvalidOperationException(Strings.SingleResourceExpected); 
                    }
                } 
                else
                {
                    this.WriteLinkCollection(queryResults, hasMoved);
                } 

                this.PopSegmentName(); 
            } 
            else if (this.requestDescription.IsSingleResult)
            { 
                Debug.Assert(hasMoved == true, "hasMoved == true");
                this.WriteTopLevelElement(expanded, queryResults.Current);
                if (queryResults.MoveNext())
                { 
                    throw new InvalidOperationException(Strings.SingleResourceExpected);
                } 
            } 
            else
            { 
                this.WriteTopLevelElements(expanded, queryResults, hasMoved);
            }

            this.Flush(); 
        }
 
        /// Gets the expandable value for the specified object. 
        /// Expanded properties for the result, possibly null.
        /// Object with value to retrieve. 
        /// Property for which value will be retrieved.
        /// The property value.
        protected static object GetExpandedProperty(IExpandedResult expanded, object customObject, ResourceProperty property)
        { 
            Debug.Assert(property != null, "property != null");
            if (expanded == null) 
            { 
                return property.GetValue(customObject);
            } 
            else
            {
                return expanded.GetExpandedPropertyValue(property.Name);
            } 
        }
 
        /// Flushes the writer to the underlying stream. 
        protected abstract void Flush();
 
        /// Writes a single top-level element.
        /// Expanded properties for the result.
        /// Element to write, possibly null.
        protected abstract void WriteTopLevelElement(IExpandedResult expanded, object element); 

        /// Writes multiple top-level elements, possibly none. 
        /// Expanded properties for the result. 
        /// Result elements.
        /// Whether  was succesfully advanced to the first element. 
        protected abstract void WriteTopLevelElements(IExpandedResult expanded, IEnumerator elements, bool hasMoved);

        /// 
        /// Write out the uri for the given element 
        /// 
        /// element whose uri needs to be written out. 
        protected abstract void WriteLink(object element); 

        ///  
        /// Write out the uri for the given elements
        /// 
        /// elements whose uri need to be writtne out
        /// the current state of the enumerator. 
        protected abstract void WriteLinkCollection(IEnumerator elements, bool hasMoved);
 
        ///  
        /// Adds the given object instance to complex type collection
        ///  
        /// instance to be added
        /// true, if it got added successfully
        protected bool AddToComplexTypeCollection(object complexTypeInstance)
        { 
            if (this.complexTypeCollection == null)
            { 
                this.complexTypeCollection = new HashSet(ObjectReferenceEqualityComparer.Instance); 
            }
 
            return this.complexTypeCollection.Add(complexTypeInstance);
        }

        /// Gets the  for the  object. 
        /// Object being written.
        /// Resource container for current object. 
        /// This method relies on segments being pushed and popped for MEST scenarios. 
        protected ResourceContainer GetContainerForCurrent(object current)
        { 
            Debug.Assert(current != null, "current != null");
            ResourceContainer result = this.CurrentContainer;
            if (result == null)
            { 
                result = this.Provider.GetContainerForResourceType(current.GetType());
            } 
 
            return result;
        } 

#if ASTORIA_OPEN_OBJECT

        /// Pushes a segment for the root of the tree being written out. 
        /// Name of open property.
        /// Resulved type of open property. 
        /// Calls to this method should be balanced with calls to PopSegmentName. 
        protected void PushSegmentForOpenProperty(string propertyName, ResourceType propertyResourceType)
        { 
            Debug.Assert(propertyName != null, "propertyName != null");
            Debug.Assert(propertyResourceType != null, "propertyResourceType != null");
            ResourceContainer container = this.provider.GetContainerForResourceType(propertyResourceType.Type);
            this.PushSegment(propertyName, container); 
        }
 
#endif 

        /// Increments the result count for the current segment, throws if exceeds the limit. 
        protected void IncrementSegmentResultCount()
        {
            if (this.segmentResultCounts != null)
            { 
                Debug.Assert(this.segmentResultCounts.Count > 0, "this.segmentResultCounts.Count > 0 -- otherwise we didn't PushSegmentForRoot");
                int max = this.configuration.MaxResultsPerCollection; 
                if (max != Int32.MaxValue) 
                {
                    int count = this.segmentResultCounts[this.segmentResultCounts.Count - 1]; 
                    checked
                    {
                        count++;
                    } 

                    if (count > max) 
                    { 
                        throw DataServiceException.CreateBadRequestError(Strings.DataServiceException_GeneralError);
                    } 

                    this.segmentResultCounts[this.segmentResultCounts.Count - 1] = count;
                }
            } 
        }
 
        /// Pushes a segment from the stack of names being written. 
        /// Property to push.
        /// Calls to this method should be balanced with calls to PopSegmentName. 
        protected void PushSegmentForProperty(ResourceProperty property)
        {
            Debug.Assert(property != null, "property != null");
            ResourceContainer current = null; 
            if ((property.Kind & (ResourcePropertyKind.ResourceReference | ResourcePropertyKind.ResourceSetReference)) != 0)
            { 
                current = this.CurrentContainer; 
                if (current != null)
                { 
                    current = this.provider.GetContainer(current.Name, current.ResourceType.Type, property);
                }
            }
 
            this.PushSegment(property.Name, current);
        } 
 
        /// Pushes a segment for the root of the tree being written out.
        /// Calls to this method should be balanced with calls to PopSegmentName. 
        protected void PushSegmentForRoot()
        {
            this.PushSegment(this.RequestDescription.ContainerName, this.CurrentContainer);
        } 

        /// Pops a segment name from the stack of names being written. 
        /// Calls to this method should be balanced with previous calls to PushSegmentName. 
        protected void PopSegmentName()
        { 
            if (this.segmentNames != null)
            {
                Debug.Assert(this.segmentNames.Count > 0, "this.segmentNames.Count > 0");
                this.segmentNames.RemoveAt(this.segmentNames.Count - 1); 
                this.segmentContainers.RemoveAt(this.segmentContainers.Count - 1);
                this.segmentResultCounts.RemoveAt(this.segmentResultCounts.Count - 1); 
                Debug.Assert( 
                    this.segmentContainers.Count == this.segmentNames.Count,
                    "this.segmentContainers.Count == this.segmentNames.Count -- should always be one-to-one"); 
                Debug.Assert(
                    this.segmentContainers.Count == this.segmentResultCounts.Count,
                    "this.segmentContainers.Count == this.segmentResultCounts.Count -- should always be one-to-one");
            } 
        }
 
        /// Returns a clone of the segment names and containers trackd at this moment. 
        /// A clone of the segment names and containers tracked at this moment; possibly null.
        protected object SaveSegmentNames() 
        {
            if (this.segmentNames == null)
            {
                return null; 
            }
            else 
            { 
                return new object[3]
                { 
                    new List(this.segmentNames),
                    new List(this.segmentContainers),
                    new List(this.segmentResultCounts)
                }; 
            }
        } 
 
        /// Restores the segment names saved through .
        /// Value returned from a previous call to . 
        protected void RestoreSegmentNames(object savedSegmentNames)
        {
            object[] savedLists = (object[])savedSegmentNames;
            if (savedLists == null) 
            {
                this.segmentNames = null; 
                this.segmentContainers = null; 
                this.segmentResultCounts = null;
            } 
            else
            {
                this.segmentNames = (List)savedLists[0];
                this.segmentContainers = (List)savedLists[1]; 
                this.segmentResultCounts = (List)savedLists[2];
            } 
        } 

        ///  
        /// Remove the given object instance from the complex type collection
        /// 
        /// instance to be removed
        protected void RemoveFromComplexTypeCollection(object complexTypeInstance) 
        {
            Debug.Assert(this.complexTypeCollection != null, "this.complexTypeCollection != null"); 
            Debug.Assert(this.complexTypeCollection.Contains(complexTypeInstance), "this.complexTypeCollection.Contains(complexTypeInstance)"); 

            this.complexTypeCollection.Remove(complexTypeInstance); 
        }

        /// Checks whether the property with the specified name should be expanded in-line.
        /// Name of property to consider for expansion. 
        /// true if the segment should be expanded; false otherwise.
        protected bool ShouldExpandSegment(string name) 
        { 
            Debug.Assert(name != null, "name != null");
 
            if (this.segmentNames == null)
            {
                return false;
            } 

            for (int i = 0; i < this.requestDescription.ExpandPaths.Count; i++) 
            { 
                List expandPath = this.requestDescription.ExpandPaths[i];
                if (expandPath.Count < this.segmentNames.Count) 
                {
                    continue;
                }
 
                // We start off at '1' for segment names because the first one is the
                // "this" in the query (/Customers?$expand=Orders doesn't include "Customers"). 
                bool matchFound = true; 
                for (int j = 1; j < this.segmentNames.Count; j++)
                { 
                    if (expandPath[j - 1].Name != this.segmentNames[j])
                    {
                        matchFound = false;
                        break; 
                    }
                } 
 
                if (matchFound && expandPath[this.segmentNames.Count - 1].Name == name)
                { 
                    return true;
                }
            }
 
            return false;
        } 
 
        /// 
        /// Returns the ETag value from the host response header 
        /// 
        /// resource whose etag value gets to be returned
        /// returns the etag value for the given resource
        protected string GetETagValue(object resource) 
        {
            if (this.requestDescription.IsSingleResult) 
            { 
                return this.httpETagHeaderValue;
            } 
            else
            {
                return WebUtil.GetETagValue(this.provider, resource, this.GetContainerForCurrent(resource));
            } 
        }
 
        /// Returns the key for the given resource. 
        /// Resource for which key value needs to be returned.
        /// Specific provider from which resource was obtained. 
        /// name of the entity container that the resource belongs to
        /// Key for the given resource, with values encoded for use in a URI.
        private static string GetObjectKey(object resource, IDataServiceProvider provider, string containerName)
        { 
            Debug.Assert(resource != null, "resource != null");
            Debug.Assert(provider != null, "provider != null"); 
            Debug.Assert(!String.IsNullOrEmpty(containerName), "container name must be specified"); 

            StringBuilder resultBuilder = new StringBuilder(); 
            resultBuilder.Append(containerName);
            resultBuilder.Append('(');
            ResourceType resourceType = provider.GetResourceType(resource.GetType());
            Debug.Assert(resourceType != null, "resourceType != null"); 
            List keyProperties = resourceType.KeyProperties;
            Debug.Assert(keyProperties.Count != 0, "every resource type must have a key"); 
            for (int i = 0; i < keyProperties.Count; i++) 
            {
                ResourceProperty property = keyProperties[i]; 
                Debug.Assert(property.IsOfKind(ResourcePropertyKind.Key), "must be key property");

                object keyValue = property.GetValue(resource);
                if (keyValue == null) 
                {
                    // null keys not supported. 
                    throw new InvalidOperationException(Strings.Serializer_NullKeysAreNotSupported(property.Name)); 
                }
 
                if (i == 0)
                {
                    if (keyProperties.Count != 1)
                    { 
                        resultBuilder.Append(property.Name);
                        resultBuilder.Append('='); 
                    } 
                }
                else 
                {
                    resultBuilder.Append(',');
                    resultBuilder.Append(property.Name);
                    resultBuilder.Append('='); 
                }
 
                string keyValueText; 
                if (!System.Data.Services.Parsing.WebConvert.TryKeyPrimitiveToString(keyValue, out keyValueText))
                { 
                    throw new InvalidOperationException(Strings.Serializer_CannotConvertValue(keyValue));
                }

                Debug.Assert(keyValueText != null, "keyValueText != null - otherwise TryKeyPrimitiveToString returned true and null value"); 
                resultBuilder.Append(System.Uri.EscapeDataString(keyValueText));
            } 
 
            resultBuilder.Append(')');
            return resultBuilder.ToString(); 
        }

        /// Pushes a segment from the stack of names being written.
        /// Name of property to push. 
        /// Container to push (possibly null).
        /// Calls to this method should be balanced with calls to PopSegmentName. 
        private void PushSegment(string name, ResourceContainer container) 
        {
            if (this.configuration.MaxResultsPerCollection != Int32.MaxValue || 
                (this.requestDescription.ExpandPaths != null &&
                 this.requestDescription.ExpandPaths.Count > 0))
            {
                if (this.segmentNames == null) 
                {
                    this.segmentNames = new List(); 
                    this.segmentContainers = new List(); 
                    this.segmentResultCounts = new List();
                } 

                Debug.Assert(
                    this.segmentContainers.Count == this.segmentNames.Count,
                    "this.segmentContainers.Count == this.segmentNames.Count -- should always be one-to-one"); 
                Debug.Assert(
                    this.segmentContainers.Count == this.segmentResultCounts.Count, 
                    "this.segmentContainers.Count == this.segmentResultCounts.Count -- should always be one-to-one"); 
                this.segmentNames.Add(name);
                this.segmentContainers.Add(container); 
                this.segmentResultCounts.Add(0);
                Debug.Assert(
                    this.segmentContainers.Count == this.segmentNames.Count,
                    "this.segmentContainers.Count == this.segmentNames.Count -- should always be one-to-one"); 
                Debug.Assert(
                    this.segmentContainers.Count == this.segmentResultCounts.Count, 
                    "this.segmentContainers.Count == this.segmentResultCounts.Count -- should always be one-to-one"); 
            }
        } 

        /// Supports a by-reference equality comparison for objects.
        internal class ObjectReferenceEqualityComparer : IEqualityComparer
        { 
            /// Singleton instance.
            private static readonly ObjectReferenceEqualityComparer instance = new ObjectReferenceEqualityComparer(); 
 
            /// Singleton instance.
            internal static ObjectReferenceEqualityComparer Instance 
            {
                get
                {
                    return ObjectReferenceEqualityComparer.instance; 
                }
            } 
 
            /// Determines whether the specified objects are equal
            /// The first object to compare. 
            /// The second object to compare.
            /// true if the specified objects are equal; otherwise, false.
            bool IEqualityComparer.Equals(object x, object y)
            { 
                return object.ReferenceEquals(x, y);
            } 
 
            /// Returns a hash code for the specified object.
            /// The Object for which a hash code is to be returned. 
            /// A hash code for the specified object.
            int IEqualityComparer.GetHashCode(object obj)
            {
                Debug.Assert(obj != null, "obj != null"); 

                return obj.GetHashCode(); 
            } 
        }
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//  
//      An abstract base serializer for various serializers
//  
// 
// @owner  [....]
//--------------------------------------------------------------------- 

namespace System.Data.Services.Serializers
{
    #region Namespaces. 

    using System; 
    using System.Collections; 
    using System.Collections.Generic;
    using System.Data.Services.Providers; 
    using System.Diagnostics;
    using System.IO;
    using System.Text;
 
    #endregion Namespaces.
 
    /// Abstract base class for all serializers. 
    internal abstract class Serializer : IExceptionWriter
    { 
        #region Private fields.

        /// Base URI from which resources should be resolved.
        private readonly Uri absoluteServiceUri; 

        /// Configuration for the service. 
        private readonly DataServiceConfiguration configuration; 

        /// Data provider from which metadata should be gathered. 
        private readonly string httpETagHeaderValue;

        /// Data provider from which metadata should be gathered.
        private readonly IDataServiceProvider provider; 

        /// Description for the requested results. 
        private readonly RequestDescription requestDescription; 

        /// Collection of complex types, used for cycle detection. 
        private HashSet complexTypeCollection;

        /// Resolved segment containers.
        private List segmentContainers; 

        /// Segment names. 
        private List segmentNames; 

        /// Result counts for segments. 
        private List segmentResultCounts;

        #endregion Private fields.
 
        /// Initializes a new base Serializer, ready to write out a description.
        /// Description for the requested results. 
        /// Base URI from which resources should be resolved. 
        /// Service with configuration and provider from which metadata should be gathered.
        /// HTTP ETag header value. 
        internal Serializer(RequestDescription requestDescription, Uri absoluteServiceUri, IDataService service, string httpETagHeaderValue)
        {
            Debug.Assert(requestDescription != null, "requestDescription != null");
            Debug.Assert(absoluteServiceUri != null, "absoluteServiceUri != null"); 
            Debug.Assert(service != null, "service != null");
 
            this.requestDescription = requestDescription; 
            this.absoluteServiceUri = absoluteServiceUri;
            this.configuration = service.Configuration; 
            this.provider = service.Provider;
            this.httpETagHeaderValue = httpETagHeaderValue;
        }
 
        /// Container for the resource being serialized (possibly null).
        protected ResourceContainer CurrentContainer 
        { 
            get
            { 
                if (this.segmentContainers == null || this.segmentContainers.Count == 0)
                {
                    return this.requestDescription.LastSegmentInfo.TargetContainer;
                } 
                else
                { 
                    return this.segmentContainers[this.segmentContainers.Count - 1]; 
                }
            } 
        }

        /// 
        /// Gets the Data provider from which metadata should be gathered. 
        /// 
        protected IDataServiceProvider Provider 
        { 
            [DebuggerStepThrough]
            get { return this.provider; } 
        }

        /// Gets the absolute URI to the service.
        protected Uri AbsoluteServiceUri 
        {
            [DebuggerStepThrough] 
            get { return this.absoluteServiceUri; } 
        }
 
        /// 
        /// Gets the RequestDescription for the request that is getting serialized.
        /// 
        protected RequestDescription RequestDescription 
        {
            [DebuggerStepThrough] 
            get 
            {
                return this.requestDescription; 
            }
        }

        /// Serializes exception information. 
        /// Description of exception to serialize.
        public abstract void WriteException(HandleExceptionArgs args); 
 
        /// 
        /// Gets the uri given the list of resource properties. This logic must be the same across all 
        /// serializers. Hence putting this in a util class
        /// 
        /// instance of the resource type whose properties needs to be returned
        /// Provider from which resource was obtained. 
        /// Container for the resource.
        /// Base URI from which resources should be resolved. 
        /// uri for the given resource 
        internal static Uri GetUri(object resource, IDataServiceProvider provider, ResourceContainer container, Uri absoluteServiceUri)
        { 
            string objectKey = GetObjectKey(resource, provider, container.Name);
            return RequestUriProcessor.AppendEscapedSegment(absoluteServiceUri, objectKey);
        }
 
        /// 
        /// Appends the given entry to the given uri 
        ///  
        /// uri to which the entry needs to be appended
        /// entry which gets appended to the given uri 
        /// new uri with the entry appended to the given uri
        internal static Uri AppendEntryToUri(Uri currentUri, string entry)
        {
            return RequestUriProcessor.AppendUnescapedSegment(currentUri, entry); 
        }
 
        ///  
        /// Handles the complete serialization for the specified .
        ///  
        /// Query results to enumerate.
        /// Whether  was succesfully advanced to the first element.
        /// 
        ///  should correspond to the RequestQuery of the 
        /// RequestDescription object passed while constructing this serializer
        /// We allow the results to be passed in 
        /// to let the query be executed earlier than at result-writing time, which 
        /// helps detect data and query errors where they can be better handled.
        ///  
        internal void WriteRequest(IEnumerator queryResults, bool hasMoved)
        {
            IEnumerable requestEnumerable = this.requestDescription.RequestEnumerable;
            Debug.Assert(requestEnumerable != null, "requestQuery != null"); 
            Debug.Assert(queryResults != null, "queryResults != null");
            IExpandedResult expanded = queryResults as IExpandedResult; 
 
            if (this.requestDescription.LinkUri)
            { 
                this.PushSegmentForRoot();
                if (this.requestDescription.IsSingleResult)
                {
                    this.WriteLink(queryResults.Current); 
                    if (queryResults.MoveNext())
                    { 
                        throw new InvalidOperationException(Strings.SingleResourceExpected); 
                    }
                } 
                else
                {
                    this.WriteLinkCollection(queryResults, hasMoved);
                } 

                this.PopSegmentName(); 
            } 
            else if (this.requestDescription.IsSingleResult)
            { 
                Debug.Assert(hasMoved == true, "hasMoved == true");
                this.WriteTopLevelElement(expanded, queryResults.Current);
                if (queryResults.MoveNext())
                { 
                    throw new InvalidOperationException(Strings.SingleResourceExpected);
                } 
            } 
            else
            { 
                this.WriteTopLevelElements(expanded, queryResults, hasMoved);
            }

            this.Flush(); 
        }
 
        /// Gets the expandable value for the specified object. 
        /// Expanded properties for the result, possibly null.
        /// Object with value to retrieve. 
        /// Property for which value will be retrieved.
        /// The property value.
        protected static object GetExpandedProperty(IExpandedResult expanded, object customObject, ResourceProperty property)
        { 
            Debug.Assert(property != null, "property != null");
            if (expanded == null) 
            { 
                return property.GetValue(customObject);
            } 
            else
            {
                return expanded.GetExpandedPropertyValue(property.Name);
            } 
        }
 
        /// Flushes the writer to the underlying stream. 
        protected abstract void Flush();
 
        /// Writes a single top-level element.
        /// Expanded properties for the result.
        /// Element to write, possibly null.
        protected abstract void WriteTopLevelElement(IExpandedResult expanded, object element); 

        /// Writes multiple top-level elements, possibly none. 
        /// Expanded properties for the result. 
        /// Result elements.
        /// Whether  was succesfully advanced to the first element. 
        protected abstract void WriteTopLevelElements(IExpandedResult expanded, IEnumerator elements, bool hasMoved);

        /// 
        /// Write out the uri for the given element 
        /// 
        /// element whose uri needs to be written out. 
        protected abstract void WriteLink(object element); 

        ///  
        /// Write out the uri for the given elements
        /// 
        /// elements whose uri need to be writtne out
        /// the current state of the enumerator. 
        protected abstract void WriteLinkCollection(IEnumerator elements, bool hasMoved);
 
        ///  
        /// Adds the given object instance to complex type collection
        ///  
        /// instance to be added
        /// true, if it got added successfully
        protected bool AddToComplexTypeCollection(object complexTypeInstance)
        { 
            if (this.complexTypeCollection == null)
            { 
                this.complexTypeCollection = new HashSet(ObjectReferenceEqualityComparer.Instance); 
            }
 
            return this.complexTypeCollection.Add(complexTypeInstance);
        }

        /// Gets the  for the  object. 
        /// Object being written.
        /// Resource container for current object. 
        /// This method relies on segments being pushed and popped for MEST scenarios. 
        protected ResourceContainer GetContainerForCurrent(object current)
        { 
            Debug.Assert(current != null, "current != null");
            ResourceContainer result = this.CurrentContainer;
            if (result == null)
            { 
                result = this.Provider.GetContainerForResourceType(current.GetType());
            } 
 
            return result;
        } 

#if ASTORIA_OPEN_OBJECT

        /// Pushes a segment for the root of the tree being written out. 
        /// Name of open property.
        /// Resulved type of open property. 
        /// Calls to this method should be balanced with calls to PopSegmentName. 
        protected void PushSegmentForOpenProperty(string propertyName, ResourceType propertyResourceType)
        { 
            Debug.Assert(propertyName != null, "propertyName != null");
            Debug.Assert(propertyResourceType != null, "propertyResourceType != null");
            ResourceContainer container = this.provider.GetContainerForResourceType(propertyResourceType.Type);
            this.PushSegment(propertyName, container); 
        }
 
#endif 

        /// Increments the result count for the current segment, throws if exceeds the limit. 
        protected void IncrementSegmentResultCount()
        {
            if (this.segmentResultCounts != null)
            { 
                Debug.Assert(this.segmentResultCounts.Count > 0, "this.segmentResultCounts.Count > 0 -- otherwise we didn't PushSegmentForRoot");
                int max = this.configuration.MaxResultsPerCollection; 
                if (max != Int32.MaxValue) 
                {
                    int count = this.segmentResultCounts[this.segmentResultCounts.Count - 1]; 
                    checked
                    {
                        count++;
                    } 

                    if (count > max) 
                    { 
                        throw DataServiceException.CreateBadRequestError(Strings.DataServiceException_GeneralError);
                    } 

                    this.segmentResultCounts[this.segmentResultCounts.Count - 1] = count;
                }
            } 
        }
 
        /// Pushes a segment from the stack of names being written. 
        /// Property to push.
        /// Calls to this method should be balanced with calls to PopSegmentName. 
        protected void PushSegmentForProperty(ResourceProperty property)
        {
            Debug.Assert(property != null, "property != null");
            ResourceContainer current = null; 
            if ((property.Kind & (ResourcePropertyKind.ResourceReference | ResourcePropertyKind.ResourceSetReference)) != 0)
            { 
                current = this.CurrentContainer; 
                if (current != null)
                { 
                    current = this.provider.GetContainer(current.Name, current.ResourceType.Type, property);
                }
            }
 
            this.PushSegment(property.Name, current);
        } 
 
        /// Pushes a segment for the root of the tree being written out.
        /// Calls to this method should be balanced with calls to PopSegmentName. 
        protected void PushSegmentForRoot()
        {
            this.PushSegment(this.RequestDescription.ContainerName, this.CurrentContainer);
        } 

        /// Pops a segment name from the stack of names being written. 
        /// Calls to this method should be balanced with previous calls to PushSegmentName. 
        protected void PopSegmentName()
        { 
            if (this.segmentNames != null)
            {
                Debug.Assert(this.segmentNames.Count > 0, "this.segmentNames.Count > 0");
                this.segmentNames.RemoveAt(this.segmentNames.Count - 1); 
                this.segmentContainers.RemoveAt(this.segmentContainers.Count - 1);
                this.segmentResultCounts.RemoveAt(this.segmentResultCounts.Count - 1); 
                Debug.Assert( 
                    this.segmentContainers.Count == this.segmentNames.Count,
                    "this.segmentContainers.Count == this.segmentNames.Count -- should always be one-to-one"); 
                Debug.Assert(
                    this.segmentContainers.Count == this.segmentResultCounts.Count,
                    "this.segmentContainers.Count == this.segmentResultCounts.Count -- should always be one-to-one");
            } 
        }
 
        /// Returns a clone of the segment names and containers trackd at this moment. 
        /// A clone of the segment names and containers tracked at this moment; possibly null.
        protected object SaveSegmentNames() 
        {
            if (this.segmentNames == null)
            {
                return null; 
            }
            else 
            { 
                return new object[3]
                { 
                    new List(this.segmentNames),
                    new List(this.segmentContainers),
                    new List(this.segmentResultCounts)
                }; 
            }
        } 
 
        /// Restores the segment names saved through .
        /// Value returned from a previous call to . 
        protected void RestoreSegmentNames(object savedSegmentNames)
        {
            object[] savedLists = (object[])savedSegmentNames;
            if (savedLists == null) 
            {
                this.segmentNames = null; 
                this.segmentContainers = null; 
                this.segmentResultCounts = null;
            } 
            else
            {
                this.segmentNames = (List)savedLists[0];
                this.segmentContainers = (List)savedLists[1]; 
                this.segmentResultCounts = (List)savedLists[2];
            } 
        } 

        ///  
        /// Remove the given object instance from the complex type collection
        /// 
        /// instance to be removed
        protected void RemoveFromComplexTypeCollection(object complexTypeInstance) 
        {
            Debug.Assert(this.complexTypeCollection != null, "this.complexTypeCollection != null"); 
            Debug.Assert(this.complexTypeCollection.Contains(complexTypeInstance), "this.complexTypeCollection.Contains(complexTypeInstance)"); 

            this.complexTypeCollection.Remove(complexTypeInstance); 
        }

        /// Checks whether the property with the specified name should be expanded in-line.
        /// Name of property to consider for expansion. 
        /// true if the segment should be expanded; false otherwise.
        protected bool ShouldExpandSegment(string name) 
        { 
            Debug.Assert(name != null, "name != null");
 
            if (this.segmentNames == null)
            {
                return false;
            } 

            for (int i = 0; i < this.requestDescription.ExpandPaths.Count; i++) 
            { 
                List expandPath = this.requestDescription.ExpandPaths[i];
                if (expandPath.Count < this.segmentNames.Count) 
                {
                    continue;
                }
 
                // We start off at '1' for segment names because the first one is the
                // "this" in the query (/Customers?$expand=Orders doesn't include "Customers"). 
                bool matchFound = true; 
                for (int j = 1; j < this.segmentNames.Count; j++)
                { 
                    if (expandPath[j - 1].Name != this.segmentNames[j])
                    {
                        matchFound = false;
                        break; 
                    }
                } 
 
                if (matchFound && expandPath[this.segmentNames.Count - 1].Name == name)
                { 
                    return true;
                }
            }
 
            return false;
        } 
 
        /// 
        /// Returns the ETag value from the host response header 
        /// 
        /// resource whose etag value gets to be returned
        /// returns the etag value for the given resource
        protected string GetETagValue(object resource) 
        {
            if (this.requestDescription.IsSingleResult) 
            { 
                return this.httpETagHeaderValue;
            } 
            else
            {
                return WebUtil.GetETagValue(this.provider, resource, this.GetContainerForCurrent(resource));
            } 
        }
 
        /// Returns the key for the given resource. 
        /// Resource for which key value needs to be returned.
        /// Specific provider from which resource was obtained. 
        /// name of the entity container that the resource belongs to
        /// Key for the given resource, with values encoded for use in a URI.
        private static string GetObjectKey(object resource, IDataServiceProvider provider, string containerName)
        { 
            Debug.Assert(resource != null, "resource != null");
            Debug.Assert(provider != null, "provider != null"); 
            Debug.Assert(!String.IsNullOrEmpty(containerName), "container name must be specified"); 

            StringBuilder resultBuilder = new StringBuilder(); 
            resultBuilder.Append(containerName);
            resultBuilder.Append('(');
            ResourceType resourceType = provider.GetResourceType(resource.GetType());
            Debug.Assert(resourceType != null, "resourceType != null"); 
            List keyProperties = resourceType.KeyProperties;
            Debug.Assert(keyProperties.Count != 0, "every resource type must have a key"); 
            for (int i = 0; i < keyProperties.Count; i++) 
            {
                ResourceProperty property = keyProperties[i]; 
                Debug.Assert(property.IsOfKind(ResourcePropertyKind.Key), "must be key property");

                object keyValue = property.GetValue(resource);
                if (keyValue == null) 
                {
                    // null keys not supported. 
                    throw new InvalidOperationException(Strings.Serializer_NullKeysAreNotSupported(property.Name)); 
                }
 
                if (i == 0)
                {
                    if (keyProperties.Count != 1)
                    { 
                        resultBuilder.Append(property.Name);
                        resultBuilder.Append('='); 
                    } 
                }
                else 
                {
                    resultBuilder.Append(',');
                    resultBuilder.Append(property.Name);
                    resultBuilder.Append('='); 
                }
 
                string keyValueText; 
                if (!System.Data.Services.Parsing.WebConvert.TryKeyPrimitiveToString(keyValue, out keyValueText))
                { 
                    throw new InvalidOperationException(Strings.Serializer_CannotConvertValue(keyValue));
                }

                Debug.Assert(keyValueText != null, "keyValueText != null - otherwise TryKeyPrimitiveToString returned true and null value"); 
                resultBuilder.Append(System.Uri.EscapeDataString(keyValueText));
            } 
 
            resultBuilder.Append(')');
            return resultBuilder.ToString(); 
        }

        /// Pushes a segment from the stack of names being written.
        /// Name of property to push. 
        /// Container to push (possibly null).
        /// Calls to this method should be balanced with calls to PopSegmentName. 
        private void PushSegment(string name, ResourceContainer container) 
        {
            if (this.configuration.MaxResultsPerCollection != Int32.MaxValue || 
                (this.requestDescription.ExpandPaths != null &&
                 this.requestDescription.ExpandPaths.Count > 0))
            {
                if (this.segmentNames == null) 
                {
                    this.segmentNames = new List(); 
                    this.segmentContainers = new List(); 
                    this.segmentResultCounts = new List();
                } 

                Debug.Assert(
                    this.segmentContainers.Count == this.segmentNames.Count,
                    "this.segmentContainers.Count == this.segmentNames.Count -- should always be one-to-one"); 
                Debug.Assert(
                    this.segmentContainers.Count == this.segmentResultCounts.Count, 
                    "this.segmentContainers.Count == this.segmentResultCounts.Count -- should always be one-to-one"); 
                this.segmentNames.Add(name);
                this.segmentContainers.Add(container); 
                this.segmentResultCounts.Add(0);
                Debug.Assert(
                    this.segmentContainers.Count == this.segmentNames.Count,
                    "this.segmentContainers.Count == this.segmentNames.Count -- should always be one-to-one"); 
                Debug.Assert(
                    this.segmentContainers.Count == this.segmentResultCounts.Count, 
                    "this.segmentContainers.Count == this.segmentResultCounts.Count -- should always be one-to-one"); 
            }
        } 

        /// Supports a by-reference equality comparison for objects.
        internal class ObjectReferenceEqualityComparer : IEqualityComparer
        { 
            /// Singleton instance.
            private static readonly ObjectReferenceEqualityComparer instance = new ObjectReferenceEqualityComparer(); 
 
            /// Singleton instance.
            internal static ObjectReferenceEqualityComparer Instance 
            {
                get
                {
                    return ObjectReferenceEqualityComparer.instance; 
                }
            } 
 
            /// Determines whether the specified objects are equal
            /// The first object to compare. 
            /// The second object to compare.
            /// true if the specified objects are equal; otherwise, false.
            bool IEqualityComparer.Equals(object x, object y)
            { 
                return object.ReferenceEquals(x, y);
            } 
 
            /// Returns a hash code for the specified object.
            /// The Object for which a hash code is to be returned. 
            /// A hash code for the specified object.
            int IEqualityComparer.GetHashCode(object obj)
            {
                Debug.Assert(obj != null, "obj != null"); 

                return obj.GetHashCode(); 
            } 
        }
    } 
}

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