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

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//  
//      Provides a base deserializer for all deserializers.
//  
// 
// @owner [....]
//--------------------------------------------------------------------- 

namespace System.Data.Services.Serializers
{
    using System; 
    using System.Collections;
    using System.Collections.Generic; 
    using System.Data.Services.Parsing; 
    using System.Data.Services.Providers;
    using System.Diagnostics; 
    using System.IO;
    using System.Linq;
    using System.Text;
 
    /// 
    /// Provides a abstract base deserializer class 
    ///  
    internal abstract class Deserializer : IDisposable
    { 
        /// Maximum recursion limit on deserializer.
        private const int RecursionLimit = 100;

        /// Data service for which the deserializer will act. 
        private readonly IDataService service;
 
        /// Tracker for actions taken during deserialization. 
        private readonly UpdateTracker tracker;
 
        ///  Indicates whether the payload is for update or not 
        private readonly bool update;

        /// Depth of recursion. 
        private int recursionDepth;
 
        /// number of resources (entity or complex type) referred in this request. 
        private int objectCount;
 
        /// Initializes a new  for the specified stream.
        /// indicates whether this is a update operation or not
        /// Data service for which the deserializer will act.
        /// Tracker to use for modifications. 
        internal Deserializer(bool update, IDataService dataService, UpdateTracker tracker)
        { 
            Debug.Assert(dataService != null, "dataService != null"); 

            this.service = dataService; 
            this.tracker = tracker;
            this.update = update;
        }
 
        /// Initializes a new  based on a different one.
        /// Parent deserializer for the new instance. 
        internal Deserializer(Deserializer parent) 
        {
            Debug.Assert(parent != null, "parent != null"); 
            this.recursionDepth = parent.recursionDepth;
            this.service = parent.service;
            this.tracker = parent.tracker;
            this.update = parent.update; 
        }
 
        /// Tracker for actions taken during deserialization. 
        internal UpdateTracker Tracker
        { 
            [DebuggerStepThrough]
            get { return this.tracker; }
        }
 
        /// returns the content format for the deserializer
        protected abstract ContentFormat ContentFormat 
        { 
            get;
        } 

        /// Data service for which the deserializer will act.
        protected IDataService Service
        { 
            [DebuggerStepThrough]
            get { return this.service; } 
        } 

        ///  
        /// Returns true if the request method is a PUT (update) method
        /// 
        protected bool Update
        { 
            [DebuggerStepThrough]
            get { return this.update; } 
        } 

        /// Returns the current count of number of objects referred by this request. 
        protected int MaxObjectCount
        {
            get { return this.objectCount; }
        } 

        /// Releases resources held onto by this object. 
        void IDisposable.Dispose() 
        {
            this.Dispose(true); 
        }

        /// 
        /// Converts the given value to the expected type as per the deserializer rules 
        /// 
        /// value to the converted 
        /// property information whose value is the first parameter 
        /// specifies the content format of the payload
        /// object which is in [....] with the properties type 
        internal static object ConvertValues(object value, ResourceProperty property, ContentFormat contentFormat)
        {
            if (contentFormat == ContentFormat.Json)
            { 
                return JsonDeserializer.ConvertValues(value, property.Name, property.ResourceClrType);
            } 
            else if (contentFormat == ContentFormat.Atom || contentFormat == ContentFormat.PlainXml) 
            {
                return PlainXmlDeserializer.ConvertValuesForXml(value, property.Name, property.ResourceClrType); 
            }
            else
            {
                Debug.Assert( 
                    contentFormat == ContentFormat.Binary || contentFormat == ContentFormat.Text,
                    "expecting binary or text"); 
 
                // Do not do any coversions for them
                return value; 
            }
        }

        ///  
        /// Update the resource specified in the given request description
        ///  
        /// description about the request uri 
        /// data service type to which the request was made
        /// Stream from which request body should be read. 
        /// The tracked modifications.
        internal static RequestDescription HandlePutRequest(RequestDescription description, IDataService dataService, Stream stream)
        {
            Debug.Assert(stream != null, "stream != null"); 
            Debug.Assert(dataService != null, "dataService != null");
 
            object requestValue = null; 
            ContentFormat requestFormat;
            UpdateTracker tracker = UpdateTracker.CreateUpdateTracker(description, dataService.Provider); 
            object entityGettingModified = null;
            ResourceContainer container = null;
            string mimeType;
            Encoding encoding; 
            HttpProcessUtility.ReadContentType(dataService.RequestParams.ContentType, out mimeType, out encoding);
 
            // If its a primitive value that is getting modified, then we need to use the text or binary 
            // serializer depending on the mime type
            if ( 
#if ASTORIA_OPEN_OBJECT
                description.TargetKind == RequestTargetKind.OpenPropertyValue ||
#endif
                description.TargetKind == RequestTargetKind.PrimitiveValue) 
            {
                string contentType; 
                requestFormat = WebUtil.GetResponseFormatForPrimitiveValue(description.TargetElementType, out contentType); 
                if (contentType != mimeType)
                { 
                    throw new DataServiceException(415, Strings.BadRequest_InvalidContentType(dataService.Host.RequestContentType, description.TargetElementType.Name));
                }

                if (requestFormat == ContentFormat.Binary) 
                {
                    byte[] propertyValue = ReadByteStream(stream); 
                    if (description.Property != null && description.Property.Type == typeof(System.Data.Linq.Binary)) 
                    {
                        requestValue = new System.Data.Linq.Binary(propertyValue); 
                    }
                    else
                    {
                        requestValue = propertyValue; 
                    }
                } 
                else 
                {
                    Debug.Assert(requestFormat == ContentFormat.Text, "requestFormat == ContentFormat.Text"); 
                    Debug.Assert(encoding != null, "encoding != null");
                    StreamReader requestReader = new StreamReader(stream, encoding);
                    string propertyValue = Deserializer.ReadStringFromStream(requestReader);
 
                    if (description.Property != null && propertyValue != null)
                    { 
                        try 
                        {
                            // Convert the property value to the correct type 
                            requestValue = WebConvert.StringToPrimitive((string)propertyValue, description.Property.Type);
                        }
                        catch (FormatException e)
                        { 
                            throw DataServiceException.CreateBadRequestError(Strings.BadRequest_ErrorInConvertingPropertyValue(description.Property.Name, description.Property.Type), e);
                        } 
                    } 
                    else
                    { 
                        // For open types, there is no conversion required. There is not enough information to do the conversion.
                        requestValue = propertyValue;
                    }
                } 
            }
            else 
            { 
                requestFormat = WebUtil.SelectRequestFormat(mimeType, description);
                Deserializer deserializer = null; 
                try
                {
                    switch (requestFormat)
                    { 
                        case ContentFormat.Json:
                            deserializer = new JsonDeserializer(new StreamReader(stream, encoding), true /*update*/, dataService, tracker); 
                            break; 
                        case ContentFormat.Atom:
                            SyndicationFormatterFactory factory = new Atom10FormatterFactory(); 
                            deserializer = new SyndicationDeserializer(
                                stream,
                                encoding,
                                dataService, 
                                true /*update*/,
                                factory, 
                                tracker); 
                            break;
                        case ContentFormat.PlainXml: 
                            deserializer = new PlainXmlDeserializer(stream, encoding, dataService, true /*update*/, tracker);
                            break;
                        default:
                            throw new DataServiceException(415, Strings.BadRequest_UnsupportedRequestContentType(dataService.Host.RequestContentType)); 
                    }
 
                    if (description.LinkUri) 
                    {
                        string uri = deserializer.GetLinkUriFromPayload(); 

                        // No need to check for null - if the uri in the payload is /Customer(1)/BestFriend,
                        // and the value is null, it means that the user wants to set the current link to null
                        // i.e. in other words, unbind the relationship. 
                        object linkResource = deserializer.CreateObjectFromUri(null, null, uri, false /*verifyETag*/, true /*checkForNull*/);
                        entityGettingModified = deserializer.HandleBindOperation(description, linkResource); 
                        container = description.LastSegmentInfo.TargetContainer; 
                    }
                    else 
                    {
                        requestValue = deserializer.ReadEntity(description);

                        if (requestValue == null && 
                            description.LastSegmentInfo.HasKeyValues &&
                            description.TargetSource == RequestTargetSource.EntitySet) 
                        { 
                            throw DataServiceException.CreateBadRequestError(Strings.BadRequest_CannotSetTopLevelResourceToNull(description.ResultUri.OriginalString));
                        } 
                    }
                }
                finally
                { 
                    WebUtil.Dispose(deserializer);
                } 
            } 

            // Update the property value, if the request target is property 
            if (!description.LinkUri && IsQueryRequired(description, requestValue, dataService.Provider))
            {
                bool writeResponse = false;
 
                // Get the parent entity and its container and the resource to modify
                object resourceToModify = GetResourceToModify( 
                    description, dataService, false /*allowCrossReferencing*/, out entityGettingModified, out container); 

                // 

                object actualEntityResource = dataService.Provider.ResolveResource(entityGettingModified);

                // compare the etag for the parent entity 
                WebUtil.CompareAndGetETag(
                    actualEntityResource, entityGettingModified, container, dataService.Provider, dataService.RequestParams, out writeResponse); 
#if ASTORIA_OPEN_OBJECT 
                if (description.TargetKind != RequestTargetKind.OpenProperty &&
                    description.TargetKind != RequestTargetKind.OpenPropertyValue) 
#endif
                {
                    tracker.TrackAction(entityGettingModified, container, UpdateOperations.Change);
                } 

                Deserializer.ModifyResource(description, resourceToModify, requestValue, requestFormat, dataService.Provider); 
            } 

            if (tracker != null) 
            {
                tracker.FireNotifications(dataService.Instance);
            }
 
            if (entityGettingModified == null)
            { 
                entityGettingModified = requestValue; 
                container = description.LastSegmentInfo.TargetContainer;
            } 

            return RequestDescription.CreateSingleResultRequestDescription(description, entityGettingModified, container);
        }
 
        /// 
        /// Gets the resource to modify. 
        ///  
        /// description about the target request
        /// data service type to which the request was made 
        /// entity resource which is getting modified.
        /// Returns the object that needs to get modified
        internal static object GetResourceToModify(RequestDescription description, IDataService service, out object entityResource)
        { 
            ResourceContainer container;
            return GetResourceToModify(description, service, false /*allowCrossReference*/, out entityResource, out container); 
        } 

        ///  
        /// Returns the last segment info whose target request kind is resource
        /// 
        /// description about the target request
        /// data service type to which the request was made 
        /// whether cross-referencing is allowed for the resource in question.
        /// entity resource which is getting modified. 
        /// entity container of the entity which is getting modified. 
        /// Returns the object that needs to get modified
        internal static object GetResourceToModify(RequestDescription description, IDataService service, bool allowCrossReferencing, out object entityResource, out ResourceContainer entityContainer) 
        {
            Debug.Assert(description.SegmentInfos.Length >= 2, "description.SegmentInfos.Length >= 2");
            int modifyingResourceIndex;
 
            if (!allowCrossReferencing && description.RequestEnumerable == null)
            { 
                throw DataServiceException.CreateBadRequestError(Strings.BadRequest_ResourceCanBeCrossReferencedOnlyForBindOperation); 
            }
 
            // Set the index of the modifying resource
            if (
#if ASTORIA_OPEN_OBJECT
                description.TargetKind == RequestTargetKind.OpenPropertyValue || 
#endif
                description.TargetKind == RequestTargetKind.PrimitiveValue) 
            { 
                modifyingResourceIndex = description.SegmentInfos.Length - 3;
            } 
            else
            {
                modifyingResourceIndex = description.SegmentInfos.Length - 2;
            } 

            // Get the index of the entity resource that is getting modified 
            int entityResourceIndex = -1; 
            if (description.LinkUri)
            { 
                entityResourceIndex = modifyingResourceIndex;
            }
            else
            { 
                for (int j = modifyingResourceIndex; j >= 0; j--)
                { 
                    if (description.SegmentInfos[j].TargetKind == RequestTargetKind.Resource || 
                        description.SegmentInfos[j].HasKeyValues)
                    { 
                        entityResourceIndex = j;
                        break;
                    }
                } 
            }
 
            Debug.Assert(entityResourceIndex != -1, "This method should never be called for request that doesn't have a parent resource"); 
            entityContainer = description.SegmentInfos[entityResourceIndex].TargetContainer;
 
            if (entityContainer != null)
            {
                // Since this is the entity which is going to get modified, then we need to check for rights
                if (service.RequestParams.AstoriaHttpVerb == AstoriaVerbs.PUT) 
                {
                    service.Configuration.CheckResourceRights(entityContainer, EntitySetRights.WriteReplace); 
                } 
                else if (service.RequestParams.AstoriaHttpVerb == AstoriaVerbs.MERGE)
                { 
                    service.Configuration.CheckResourceRights(entityContainer, EntitySetRights.WriteMerge);
                }
                else
                { 
                    Debug.Assert(
                        service.RequestParams.AstoriaHttpVerb == AstoriaVerbs.POST || 
                        service.RequestParams.AstoriaHttpVerb == AstoriaVerbs.DELETE, 
                        "expecting POST and DELETE methods");
                    service.Configuration.CheckResourceRights(entityContainer, EntitySetRights.WriteMerge | EntitySetRights.WriteReplace); 
                }
            }

            entityResource = service.GetResource(description, entityResourceIndex, null); 
            if (entityResource == null)
            { 
                throw DataServiceException.CreateBadRequestError(Strings.BadRequest_DereferencingNullPropertyValue(description.SegmentInfos[entityResourceIndex].Identifier)); 
            }
 
            // now walk from the entity resource to the resource to modify.
            // for open types, as you walk, if the intermediate resource is an entity,
            // update the entityResource accordingly.
            object resourceToModify = entityResource; 
            for (int i = entityResourceIndex + 1; i <= modifyingResourceIndex; i++)
            { 
                resourceToModify = service.Provider.GetValue(resourceToModify, description.SegmentInfos[i].Identifier); 

#if ASTORIA_OPEN_OBJECT 
                if (description.TargetKind == RequestTargetKind.OpenProperty ||
                    description.TargetKind == RequestTargetKind.OpenPropertyValue)
                {
                    object resource = service.Provider.ResolveResource(resourceToModify); 
                    if (resource == null)
                    { 
                        throw DataServiceException.CreateBadRequestError(Strings.BadRequest_DereferencingNullPropertyValue(description.SegmentInfos[i].Identifier)); 
                    }
 
                    ResourceType resourceType = service.Provider.GetResourceType(resource.GetType());
                    if (resourceType == null)
                    {
                        throw new InvalidOperationException(Strings.BadProvider_InvalidTypeSpecified(resource.GetType().FullName)); 
                    }
 
                    if (resourceType.ResourceTypeKind == ResourceTypeKind.EntityType) 
                    {
                        entityResource = resource; 
                        entityContainer = service.Provider.GetContainerForResourceType(resourceType.Type);
                    }
                }
#endif 
            }
 
#if ASTORIA_OPEN_OBJECT 
            if (entityContainer == null)
            { 
                Debug.Assert(
                    description.TargetKind == RequestTargetKind.OpenProperty ||
                    description.TargetKind == RequestTargetKind.OpenPropertyValue,
                    "its a open property target. Hence resource container must be null"); 
                object resource = service.Provider.ResolveResource(entityResource);
                entityContainer = service.Provider.GetContainerForResourceType(resource.GetType()); 
            } 
#else
            Debug.Assert(entityContainer != null, "entityContainer != null"); 
#endif

            return resourceToModify;
        } 

        ///  
        /// Modify the value of the given resource to the given value 
        /// 
        /// description about the request 
        /// resource that needs to be modified
        /// the new value for the target resource
        /// specifies the content format of the payload
        /// provider for this request 
        internal static void ModifyResource(RequestDescription description, object resourceToBeModified, object requestValue, ContentFormat contentFormat, IDataServiceProvider provider)
        { 
#if ASTORIA_OPEN_OBJECT 
            if ((description.TargetKind == RequestTargetKind.OpenProperty && !description.LastSegmentInfo.HasKeyValues) ||
                description.TargetKind == RequestTargetKind.OpenPropertyValue) 
            {
                SetOpenPropertyValue(resourceToBeModified, description.ContainerName, requestValue, provider);
            }
            else 
#endif
            { 
                Debug.Assert( 
                    description.TargetKind == RequestTargetKind.Primitive ||
                    description.TargetKind == RequestTargetKind.ComplexObject || 
                    description.TargetKind == RequestTargetKind.PrimitiveValue,
                    "unexpected target kind encountered");

                // update the primitive value 
                ResourceProperty propertyToUpdate = description.LastSegmentInfo.ProjectedProperty;
                SetPropertyValue(propertyToUpdate, resourceToBeModified, requestValue, contentFormat, provider); 
            } 
        }
 
        /// 
        /// Get the resource referred by the given segment
        /// 
        /// information about the segment. 
        /// full name of the resource referred by the segment.
        /// data service type to which the request was made 
        /// whether to check if the resource is null or not. 
        /// returns the resource returned by the provider.
        internal static object GetResource(SegmentInfo segmentInfo, string fullTypeName, IDataService service, bool checkForNull) 
        {
            if (segmentInfo.TargetContainer != null)
            {
#if ASTORIA_OPEN_OBJECT 
                Debug.Assert(
                    segmentInfo.TargetKind != RequestTargetKind.OpenProperty && 
                    segmentInfo.TargetKind != RequestTargetKind.OpenPropertyValue, 
                    "container can be null only for open types");
#endif 

                service.Configuration.CheckResourceRights(segmentInfo.TargetContainer, EntitySetRights.ReadSingle);
            }
 
            object resource = service.Provider.GetResource((IQueryable)segmentInfo.RequestEnumerable, fullTypeName);
            if (resource == null && 
                (segmentInfo.HasKeyValues || checkForNull)) 
            {
                throw DataServiceException.CreateResourceNotFound(segmentInfo.Identifier); 
            }

            return resource;
        } 

        ///  
        /// Deserializes the given stream into clr object as specified in the payload 
        /// 
        /// description about the target request 
        /// the object instance that it created and populated from the reader
        internal object ReadEntity(RequestDescription requestDescription)
        {
            // If the description points to a resource, 
            // we need to materialize the object and return back.
            SegmentInfo segmentInfo = requestDescription.LastSegmentInfo; 
            if (!this.Update) 
            {
                Debug.Assert(!segmentInfo.SingleResult, "POST operation is allowed only on collections"); 
                SegmentInfo adjustedSegment = new SegmentInfo();
                adjustedSegment.TargetKind = segmentInfo.TargetKind;
                adjustedSegment.TargetSource = segmentInfo.TargetSource;
                adjustedSegment.SingleResult = true; 
                adjustedSegment.ProjectedProperty = segmentInfo.ProjectedProperty;
                adjustedSegment.TargetElementType = segmentInfo.TargetElementType; 
                adjustedSegment.TargetContainer = segmentInfo.TargetContainer; 
                adjustedSegment.Identifier = segmentInfo.Identifier;
                segmentInfo = adjustedSegment; 
            }

            return this.CreateSingleObject(segmentInfo);
        } 

        ///  
        /// Handle bind operation 
        /// 
        /// information about the request uri. 
        /// the child resource which needs to be linked.
        /// returns the parent object to which an new object was linked to.
        internal object HandleBindOperation(RequestDescription description, object linkResource)
        { 
            Debug.Assert(description != null, "description != null");
 
            object entityGettingModified; 
            ResourceContainer container;
 
            object resourceToBeModified = Deserializer.GetResourceToModify(description, this.Service, true /*allowCrossReference*/, out entityGettingModified, out container);
            Debug.Assert(resourceToBeModified == entityGettingModified, "Since this is a link operation, modifying resource must be the entity resource");

#if ASTORIA_OPEN_OBJECT 
            if (description.TargetKind != RequestTargetKind.OpenProperty)
#endif 
            { 
                this.tracker.TrackAction(entityGettingModified, container, UpdateOperations.Change);
            } 

            if (description.IsSingleResult)
            {
                this.Service.Provider.SetReference(entityGettingModified, description.ContainerName, linkResource); 
            }
            else 
            { 
                this.Service.Provider.AddReferenceToCollection(entityGettingModified, description.ContainerName, linkResource);
            } 

            return entityGettingModified;
        }
 
        /// 
        /// Handles post request. 
        ///  
        /// description about the uri for the post operation.
        /// returns the resource that is getting inserted or binded - as specified in the payload. 
        internal object HandlePostRequest(RequestDescription requestDescription)
        {
            Debug.Assert(!this.Update, "This method must be called for POST operations only");
            Debug.Assert(requestDescription != null, "requestDescription != null"); 
            object resourceInPayload;
 
            if (requestDescription.LinkUri) 
            {
                string uri = this.GetLinkUriFromPayload(); 
                resourceInPayload = this.CreateObjectFromUri(null, null, uri, false/*verifyETag*/, true /*checkForNull*/);
                Debug.Assert(resourceInPayload != null, "link resource cannot be null");
                this.HandleBindOperation(requestDescription, resourceInPayload);
            } 
            else
            { 
                if (requestDescription.LastSegmentInfo.TargetContainer != null) 
                {
                    this.Service.Configuration.CheckResourceRights(requestDescription.LastSegmentInfo.TargetContainer, EntitySetRights.WriteAppend); 
                }

                resourceInPayload = this.ReadEntity(requestDescription);
                if (requestDescription.TargetSource == RequestTargetSource.Property) 
                {
                    Debug.Assert( 
#if ASTORIA_OPEN_OBJECT 
                        (requestDescription.TargetKind == RequestTargetKind.OpenProperty && !requestDescription.IsSingleResult) ||
#endif 
                        requestDescription.Property.Kind == ResourcePropertyKind.ResourceSetReference,
                        "Expecting POST resource set property");
                    this.HandleBindOperation(requestDescription, resourceInPayload);
                } 
                else
                { 
                    Debug.Assert(requestDescription.TargetSource == RequestTargetSource.EntitySet, "Expecting POST on entity set"); 
                    this.tracker.TrackAction(resourceInPayload, requestDescription.LastSegmentInfo.TargetContainer, UpdateOperations.Add);
                } 
            }

            return resourceInPayload;
        } 

        ///  
        /// Update the object count value to the given value. 
        /// 
        /// value to be set for object count. 
        internal void UpdateObjectCount(int value)
        {
            Debug.Assert(0 <= value, "MaxObjectCount cannot be initialized to a negative number");
            Debug.Assert(value <= this.Service.Configuration.MaxObjectCountOnInsert, "On initialize, the value should be less than max object count"); 
            this.objectCount = value;
        } 
 
        /// Creates a new SegmentInfo for the specified .
        /// Property to create segment info for (possibly null). 
        /// Name for the property.
        /// Whether a single result is expected.
        /// 
        /// A new  instance that describes the specfied  
        /// as a target, or an open proprty if  is null.
        ///  
        protected static SegmentInfo CreateSegment(ResourceProperty property, string propertyName, bool singleResult) 
        {
            SegmentInfo result = new SegmentInfo(); 
            result.TargetSource = RequestTargetSource.Property;
            result.SingleResult = singleResult;
            result.Identifier = propertyName;
#if ASTORIA_OPEN_OBJECT 
            if (property == null)
            { 
                result.TargetKind = RequestTargetKind.OpenProperty; 
            }
            else 
#else
            Debug.Assert(property != null, "property != null");
#endif
            { 
                result.TargetKind = RequestTargetKind.Resource;
                result.Identifier = propertyName; 
                result.ProjectedProperty = property; 
                result.TargetElementType = property.ResourceClrType;
                result.TargetContainer = property.ResourceContainer; 
            }

            return result;
        } 

        ///  
        /// Set the value of the given resource property to the new value 
        /// 
        /// property whose value needs to be updated 
        /// instance of the declaring type of the property for which the property value needs to be updated
        /// new value for the property
        /// specifies the content format of the payload
        /// provider for this request 
        protected static void SetPropertyValue(ResourceProperty resourceProperty, object declaringResource, object propertyValue, ContentFormat contentFormat, IDataServiceProvider provider)
        { 
            Debug.Assert( 
                resourceProperty.TypeKind == ResourceTypeKind.ComplexType ||
                resourceProperty.TypeKind == ResourceTypeKind.Primitive, 
                "Only primitive and complex type values must be set via this method");

            // For open types, resource property can be null
            if (resourceProperty.TypeKind == ResourceTypeKind.Primitive) 
            {
                // First convert the value of the property to the expected type, as specified in the resource property 
                propertyValue = ConvertValues(propertyValue, resourceProperty, contentFormat); 
            }
 
            provider.SetValue(declaringResource, resourceProperty.Name, propertyValue);
        }

#if ASTORIA_OPEN_OBJECT 

        ///  
        /// Set the value of the open property 
        /// 
        /// instance of the declaring type of the property for which the property value needs to be updated 
        /// name of the open property to update
        /// new value for the property
        /// provider for this request
        protected static void SetOpenPropertyValue(object declaringResource, string propertyName, object propertyValue, IDataServiceProvider provider) 
        {
            provider.SetValue(declaringResource, propertyName, propertyValue); 
        } 

#endif 

        /// 
        /// Reads the content from the stream reader and returns it as string
        ///  
        /// stream reader from which the content needs to be read
        /// string containing the content as read from the stream reader 
        protected static string ReadStringFromStream(StreamReader streamReader) 
        {
            return streamReader.ReadToEnd(); 
        }

        /// 
        /// Make sure binding operations cannot be performed in PUT operations 
        /// 
        /// http method name for the request. 
        protected static void CheckForBindingInPutOperations(AstoriaVerbs requestVerb) 
        {
            // Cannot bind in PUT operations. 
            if (requestVerb == AstoriaVerbs.PUT)
            {
                throw DataServiceException.CreateBadRequestError(Strings.DataServiceException_GeneralError);
            } 
        }
 
        ///  
        /// Create the object from the given payload and return the top level object
        ///  
        /// info about the object being created
        /// instance of the object created
        protected abstract object CreateSingleObject(SegmentInfo segmentInfo);
 
        /// 
        /// Get the resource referred by the uri in the payload 
        ///  
        /// resource referred by the uri in the payload.
        protected abstract string GetLinkUriFromPayload(); 

        /// Provides an opportunity to clean-up resources.
        /// 
        /// Whether the call is being made from an explicit call to 
        /// IDisposable.Dispose() rather than through the finalizer.
        ///  
        protected virtual void Dispose(bool disposing) 
        {
        } 

        /// Marks the fact that a recursive method was entered, and checks that the depth is allowed.
        protected void RecurseEnter()
        { 
            this.recursionDepth++;
            Debug.Assert(this.recursionDepth <= RecursionLimit, "this.recursionDepth <= recursionLimit"); 
            if (this.recursionDepth == RecursionLimit) 
            {
                // 400 - Bad Request 
                throw DataServiceException.CreateDeepRecursion(RecursionLimit);
            }
        }
 
        /// Marks the fact that a recursive method is leaving..
        protected void RecurseLeave() 
        { 
            this.recursionDepth--;
            Debug.Assert(0 <= this.recursionDepth, "0 <= this.recursionDepth"); 
            Debug.Assert(this.recursionDepth < RecursionLimit, "this.recursionDepth < recursionLimit");
        }

        ///  
        /// Create a new instance of the given resource type and populate the key properties from the segment info
        ///  
        /// resource type whose instance needs to be created 
        /// segment info containing the description of the uri
        /// uri which contains the key for the given resource type 
        /// verify etag value of the current resource with one specified in the request header
        /// validate that the resource cannot be null.
        /// a new instance of the given resource type with key values populated
        protected object CreateObjectFromUri( 
            ResourceType resourceType,
            SegmentInfo segmentInfo, 
            string uri, 
            bool verifyETag,
            bool checkForNull) 
        {
            RequestDescription description = null;
            if (uri != null)
            { 
                Uri referencedUri = RequestUriProcessor.GetAbsoluteUriFromReference(uri, this.Service.RequestParams);
                description = RequestUriProcessor.ProcessRequestUri(referencedUri, this.Service); 
            } 

            return this.CreateObjectFromUri(resourceType, segmentInfo, description, verifyETag, checkForNull, false /*replaceResource*/); 
        }

        /// 
        /// Create a new instance of the given resource type and populate the key properties from the segment info 
        /// 
        /// resource type whose instance needs to be created 
        /// segment info containing the description of the uri 
        /// information about the uri in the payload.
        /// verify etag value of the current resource with one specified in the request header 
        /// validate that the resource cannot be null.
        /// reset the resource as referred by the segment.
        /// a new instance of the given resource type with key values populated
        protected object CreateObjectFromUri( 
            ResourceType resourceType,
            SegmentInfo segmentInfo, 
            RequestDescription description, 
            bool verifyETag,
            bool checkForNull, 
            bool replaceResource)
        {
            Debug.Assert(resourceType == null && !verifyETag || resourceType != null, "For etag verification, resource type must be specified");
 
            if (description != null)
            { 
                segmentInfo = description.LastSegmentInfo; 
            }
            else if (segmentInfo.RequestEnumerable == null) 
            {
                throw DataServiceException.CreateBadRequestError(Strings.BadRequest_ResourceCanBeCrossReferencedOnlyForBindOperation);
            }
 
            ResourceContainer container = segmentInfo.TargetContainer;
 
            object resourceCookie; 
            string typeName = resourceType != null ? resourceType.FullName : null;
            string containerName = container != null ? container.Name : null; 

            if (description != null)
            {
                resourceCookie = this.Service.GetResource(description, description.SegmentInfos.Length - 1, typeName); 
                if (checkForNull)
                { 
                    WebUtil.CheckResourceExists(resourceCookie != null, description.LastSegmentInfo.Identifier); 
                }
            } 
            else
            {
                resourceCookie = Deserializer.GetResource(segmentInfo, typeName, this.Service, checkForNull);
            } 

            if (resourceType != null) 
            { 
                ICollection etagProperties = this.Service.Provider.GetETagProperties(containerName, resourceType.Type);
                if (etagProperties.Count == 0) 
                {
                    if (this.service.RequestParams.IfMatch != null)
                    {
                        throw DataServiceException.CreateBadRequestError(Strings.Serializer_NoETagPropertiesForType); 
                    }
                } 
                else if (verifyETag) 
                {
                    Debug.Assert(checkForNull, "for verifying etags, the resource must not be null"); 
                    if (this.service.RequestParams.IfMatch == null)
                    {
                        throw DataServiceException.CreateBadRequestError(Strings.DataService_CannotPerformPutOperationWithoutETag(resourceType.FullName));
                    } 

                    if (this.service.RequestParams.IfMatch != XmlConstants.HttpAnyETag) 
                    { 
                        string currentETagValue = WebUtil.GetETagValue(resourceCookie, etagProperties, this.Service.Provider);
                        if (currentETagValue != this.service.RequestParams.IfMatch) 
                        {
                            throw DataServiceException.CreatePreConditionFailedError(Strings.Serializer_ETagValueDoesNotMatch);
                        }
                    } 
                }
            } 
 
            if (replaceResource)
            { 
                Debug.Assert(checkForNull, "For resetting resource, the value cannot be null");
                resourceCookie = this.Service.Provider.ResetResource(resourceCookie);
                WebUtil.CheckResourceExists(resourceCookie != null, segmentInfo.Identifier);
            } 

            return resourceCookie; 
        } 

        ///  
        /// Check and increment the object count
        /// 
        protected void CheckAndIncrementObjectCount()
        { 
            Debug.Assert(this.Update && this.objectCount == 0 || !this.Update, "For updates, the object count is never tracked");
            Debug.Assert(this.objectCount <= this.Service.Configuration.MaxObjectCountOnInsert, "The object count should never exceed the limit"); 
 
            if (!this.Update)
            { 
                this.objectCount++;

                if (this.objectCount > this.Service.Configuration.MaxObjectCountOnInsert)
                { 
                    throw new DataServiceException(413, Strings.DataServiceException_GeneralError);
                } 
            } 
        }
 
        /// 
        /// Read the byte from the given input request stream
        /// 
        /// input/request stream from which data needs to be read 
        /// byte array containing all the data read
        private static byte[] ReadByteStream(Stream stream) 
        { 
            byte[] data;
 
            // try to read data from the stream 1k at a time
            long numberOfBytesRead = 0;
            int result = 0;
            List byteData = new List(); 

            do 
            { 
                data = new byte[4000];
                result = stream.Read(data, 0, data.Length); 
                numberOfBytesRead += result;
                byteData.Add(data);
            }
            while (result == data.Length); 

            // Find out the total number of bytes read and copy data from byteData to data 
            data = new byte[numberOfBytesRead]; 
            for (int i = 0; i < byteData.Count - 1; i++)
            { 
                Buffer.BlockCopy(byteData[i], 0, data, i * 4000, 4000);
            }

            // For the last thing, copy the remaining number of bytes, not always 4000 
            Buffer.BlockCopy(byteData[byteData.Count - 1], 0, data, (byteData.Count - 1) * 4000, result);
            return data; 
        } 

        ///  
        /// Returns true if we need to query the provider before updating.
        /// 
        /// request description
        /// value corresponding to the payload for this request 
        /// provider against which the request was targeted
        /// returns true if we need to issue an query to satishfy the request 
#if !ASTORIA_OPEN_OBJECT 
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "requestValue", Justification = "Required for open type support.")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "provider", Justification = "Required for open type support.")] 
#endif
        private static bool IsQueryRequired(RequestDescription requestDescription, object requestValue, IDataServiceProvider provider)
        {
            Debug.Assert(requestDescription.IsSingleResult, "requestDescription.IsSingleResult"); 

            if (requestDescription.TargetKind == RequestTargetKind.PrimitiveValue || 
                requestDescription.TargetKind == RequestTargetKind.Primitive || 
#if ASTORIA_OPEN_OBJECT
                requestDescription.TargetKind == RequestTargetKind.OpenPropertyValue || 
#endif
                requestDescription.TargetKind == RequestTargetKind.ComplexObject)
            {
                return true; 
            }
 
#if ASTORIA_OPEN_OBJECT 
            if (requestDescription.TargetKind == RequestTargetKind.OpenProperty &&
                !requestDescription.LastSegmentInfo.HasKeyValues) 
            {
                // if the value is null, then just set it, since we don't know the type
                if (requestValue == null || WebUtil.IsPrimitiveType(requestValue.GetType()))
                { 
                    return true;
                } 
 
                // otherwise just set the complex type properties
                if (provider.GetResourceTypeKind(requestValue.GetType()) == ResourceTypeKind.ComplexType) 
                {
                    return true;
                }
            } 
#endif
 
            return false; 
        }
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//  
//      Provides a base deserializer for all deserializers.
//  
// 
// @owner [....]
//--------------------------------------------------------------------- 

namespace System.Data.Services.Serializers
{
    using System; 
    using System.Collections;
    using System.Collections.Generic; 
    using System.Data.Services.Parsing; 
    using System.Data.Services.Providers;
    using System.Diagnostics; 
    using System.IO;
    using System.Linq;
    using System.Text;
 
    /// 
    /// Provides a abstract base deserializer class 
    ///  
    internal abstract class Deserializer : IDisposable
    { 
        /// Maximum recursion limit on deserializer.
        private const int RecursionLimit = 100;

        /// Data service for which the deserializer will act. 
        private readonly IDataService service;
 
        /// Tracker for actions taken during deserialization. 
        private readonly UpdateTracker tracker;
 
        ///  Indicates whether the payload is for update or not 
        private readonly bool update;

        /// Depth of recursion. 
        private int recursionDepth;
 
        /// number of resources (entity or complex type) referred in this request. 
        private int objectCount;
 
        /// Initializes a new  for the specified stream.
        /// indicates whether this is a update operation or not
        /// Data service for which the deserializer will act.
        /// Tracker to use for modifications. 
        internal Deserializer(bool update, IDataService dataService, UpdateTracker tracker)
        { 
            Debug.Assert(dataService != null, "dataService != null"); 

            this.service = dataService; 
            this.tracker = tracker;
            this.update = update;
        }
 
        /// Initializes a new  based on a different one.
        /// Parent deserializer for the new instance. 
        internal Deserializer(Deserializer parent) 
        {
            Debug.Assert(parent != null, "parent != null"); 
            this.recursionDepth = parent.recursionDepth;
            this.service = parent.service;
            this.tracker = parent.tracker;
            this.update = parent.update; 
        }
 
        /// Tracker for actions taken during deserialization. 
        internal UpdateTracker Tracker
        { 
            [DebuggerStepThrough]
            get { return this.tracker; }
        }
 
        /// returns the content format for the deserializer
        protected abstract ContentFormat ContentFormat 
        { 
            get;
        } 

        /// Data service for which the deserializer will act.
        protected IDataService Service
        { 
            [DebuggerStepThrough]
            get { return this.service; } 
        } 

        ///  
        /// Returns true if the request method is a PUT (update) method
        /// 
        protected bool Update
        { 
            [DebuggerStepThrough]
            get { return this.update; } 
        } 

        /// Returns the current count of number of objects referred by this request. 
        protected int MaxObjectCount
        {
            get { return this.objectCount; }
        } 

        /// Releases resources held onto by this object. 
        void IDisposable.Dispose() 
        {
            this.Dispose(true); 
        }

        /// 
        /// Converts the given value to the expected type as per the deserializer rules 
        /// 
        /// value to the converted 
        /// property information whose value is the first parameter 
        /// specifies the content format of the payload
        /// object which is in [....] with the properties type 
        internal static object ConvertValues(object value, ResourceProperty property, ContentFormat contentFormat)
        {
            if (contentFormat == ContentFormat.Json)
            { 
                return JsonDeserializer.ConvertValues(value, property.Name, property.ResourceClrType);
            } 
            else if (contentFormat == ContentFormat.Atom || contentFormat == ContentFormat.PlainXml) 
            {
                return PlainXmlDeserializer.ConvertValuesForXml(value, property.Name, property.ResourceClrType); 
            }
            else
            {
                Debug.Assert( 
                    contentFormat == ContentFormat.Binary || contentFormat == ContentFormat.Text,
                    "expecting binary or text"); 
 
                // Do not do any coversions for them
                return value; 
            }
        }

        ///  
        /// Update the resource specified in the given request description
        ///  
        /// description about the request uri 
        /// data service type to which the request was made
        /// Stream from which request body should be read. 
        /// The tracked modifications.
        internal static RequestDescription HandlePutRequest(RequestDescription description, IDataService dataService, Stream stream)
        {
            Debug.Assert(stream != null, "stream != null"); 
            Debug.Assert(dataService != null, "dataService != null");
 
            object requestValue = null; 
            ContentFormat requestFormat;
            UpdateTracker tracker = UpdateTracker.CreateUpdateTracker(description, dataService.Provider); 
            object entityGettingModified = null;
            ResourceContainer container = null;
            string mimeType;
            Encoding encoding; 
            HttpProcessUtility.ReadContentType(dataService.RequestParams.ContentType, out mimeType, out encoding);
 
            // If its a primitive value that is getting modified, then we need to use the text or binary 
            // serializer depending on the mime type
            if ( 
#if ASTORIA_OPEN_OBJECT
                description.TargetKind == RequestTargetKind.OpenPropertyValue ||
#endif
                description.TargetKind == RequestTargetKind.PrimitiveValue) 
            {
                string contentType; 
                requestFormat = WebUtil.GetResponseFormatForPrimitiveValue(description.TargetElementType, out contentType); 
                if (contentType != mimeType)
                { 
                    throw new DataServiceException(415, Strings.BadRequest_InvalidContentType(dataService.Host.RequestContentType, description.TargetElementType.Name));
                }

                if (requestFormat == ContentFormat.Binary) 
                {
                    byte[] propertyValue = ReadByteStream(stream); 
                    if (description.Property != null && description.Property.Type == typeof(System.Data.Linq.Binary)) 
                    {
                        requestValue = new System.Data.Linq.Binary(propertyValue); 
                    }
                    else
                    {
                        requestValue = propertyValue; 
                    }
                } 
                else 
                {
                    Debug.Assert(requestFormat == ContentFormat.Text, "requestFormat == ContentFormat.Text"); 
                    Debug.Assert(encoding != null, "encoding != null");
                    StreamReader requestReader = new StreamReader(stream, encoding);
                    string propertyValue = Deserializer.ReadStringFromStream(requestReader);
 
                    if (description.Property != null && propertyValue != null)
                    { 
                        try 
                        {
                            // Convert the property value to the correct type 
                            requestValue = WebConvert.StringToPrimitive((string)propertyValue, description.Property.Type);
                        }
                        catch (FormatException e)
                        { 
                            throw DataServiceException.CreateBadRequestError(Strings.BadRequest_ErrorInConvertingPropertyValue(description.Property.Name, description.Property.Type), e);
                        } 
                    } 
                    else
                    { 
                        // For open types, there is no conversion required. There is not enough information to do the conversion.
                        requestValue = propertyValue;
                    }
                } 
            }
            else 
            { 
                requestFormat = WebUtil.SelectRequestFormat(mimeType, description);
                Deserializer deserializer = null; 
                try
                {
                    switch (requestFormat)
                    { 
                        case ContentFormat.Json:
                            deserializer = new JsonDeserializer(new StreamReader(stream, encoding), true /*update*/, dataService, tracker); 
                            break; 
                        case ContentFormat.Atom:
                            SyndicationFormatterFactory factory = new Atom10FormatterFactory(); 
                            deserializer = new SyndicationDeserializer(
                                stream,
                                encoding,
                                dataService, 
                                true /*update*/,
                                factory, 
                                tracker); 
                            break;
                        case ContentFormat.PlainXml: 
                            deserializer = new PlainXmlDeserializer(stream, encoding, dataService, true /*update*/, tracker);
                            break;
                        default:
                            throw new DataServiceException(415, Strings.BadRequest_UnsupportedRequestContentType(dataService.Host.RequestContentType)); 
                    }
 
                    if (description.LinkUri) 
                    {
                        string uri = deserializer.GetLinkUriFromPayload(); 

                        // No need to check for null - if the uri in the payload is /Customer(1)/BestFriend,
                        // and the value is null, it means that the user wants to set the current link to null
                        // i.e. in other words, unbind the relationship. 
                        object linkResource = deserializer.CreateObjectFromUri(null, null, uri, false /*verifyETag*/, true /*checkForNull*/);
                        entityGettingModified = deserializer.HandleBindOperation(description, linkResource); 
                        container = description.LastSegmentInfo.TargetContainer; 
                    }
                    else 
                    {
                        requestValue = deserializer.ReadEntity(description);

                        if (requestValue == null && 
                            description.LastSegmentInfo.HasKeyValues &&
                            description.TargetSource == RequestTargetSource.EntitySet) 
                        { 
                            throw DataServiceException.CreateBadRequestError(Strings.BadRequest_CannotSetTopLevelResourceToNull(description.ResultUri.OriginalString));
                        } 
                    }
                }
                finally
                { 
                    WebUtil.Dispose(deserializer);
                } 
            } 

            // Update the property value, if the request target is property 
            if (!description.LinkUri && IsQueryRequired(description, requestValue, dataService.Provider))
            {
                bool writeResponse = false;
 
                // Get the parent entity and its container and the resource to modify
                object resourceToModify = GetResourceToModify( 
                    description, dataService, false /*allowCrossReferencing*/, out entityGettingModified, out container); 

                // 

                object actualEntityResource = dataService.Provider.ResolveResource(entityGettingModified);

                // compare the etag for the parent entity 
                WebUtil.CompareAndGetETag(
                    actualEntityResource, entityGettingModified, container, dataService.Provider, dataService.RequestParams, out writeResponse); 
#if ASTORIA_OPEN_OBJECT 
                if (description.TargetKind != RequestTargetKind.OpenProperty &&
                    description.TargetKind != RequestTargetKind.OpenPropertyValue) 
#endif
                {
                    tracker.TrackAction(entityGettingModified, container, UpdateOperations.Change);
                } 

                Deserializer.ModifyResource(description, resourceToModify, requestValue, requestFormat, dataService.Provider); 
            } 

            if (tracker != null) 
            {
                tracker.FireNotifications(dataService.Instance);
            }
 
            if (entityGettingModified == null)
            { 
                entityGettingModified = requestValue; 
                container = description.LastSegmentInfo.TargetContainer;
            } 

            return RequestDescription.CreateSingleResultRequestDescription(description, entityGettingModified, container);
        }
 
        /// 
        /// Gets the resource to modify. 
        ///  
        /// description about the target request
        /// data service type to which the request was made 
        /// entity resource which is getting modified.
        /// Returns the object that needs to get modified
        internal static object GetResourceToModify(RequestDescription description, IDataService service, out object entityResource)
        { 
            ResourceContainer container;
            return GetResourceToModify(description, service, false /*allowCrossReference*/, out entityResource, out container); 
        } 

        ///  
        /// Returns the last segment info whose target request kind is resource
        /// 
        /// description about the target request
        /// data service type to which the request was made 
        /// whether cross-referencing is allowed for the resource in question.
        /// entity resource which is getting modified. 
        /// entity container of the entity which is getting modified. 
        /// Returns the object that needs to get modified
        internal static object GetResourceToModify(RequestDescription description, IDataService service, bool allowCrossReferencing, out object entityResource, out ResourceContainer entityContainer) 
        {
            Debug.Assert(description.SegmentInfos.Length >= 2, "description.SegmentInfos.Length >= 2");
            int modifyingResourceIndex;
 
            if (!allowCrossReferencing && description.RequestEnumerable == null)
            { 
                throw DataServiceException.CreateBadRequestError(Strings.BadRequest_ResourceCanBeCrossReferencedOnlyForBindOperation); 
            }
 
            // Set the index of the modifying resource
            if (
#if ASTORIA_OPEN_OBJECT
                description.TargetKind == RequestTargetKind.OpenPropertyValue || 
#endif
                description.TargetKind == RequestTargetKind.PrimitiveValue) 
            { 
                modifyingResourceIndex = description.SegmentInfos.Length - 3;
            } 
            else
            {
                modifyingResourceIndex = description.SegmentInfos.Length - 2;
            } 

            // Get the index of the entity resource that is getting modified 
            int entityResourceIndex = -1; 
            if (description.LinkUri)
            { 
                entityResourceIndex = modifyingResourceIndex;
            }
            else
            { 
                for (int j = modifyingResourceIndex; j >= 0; j--)
                { 
                    if (description.SegmentInfos[j].TargetKind == RequestTargetKind.Resource || 
                        description.SegmentInfos[j].HasKeyValues)
                    { 
                        entityResourceIndex = j;
                        break;
                    }
                } 
            }
 
            Debug.Assert(entityResourceIndex != -1, "This method should never be called for request that doesn't have a parent resource"); 
            entityContainer = description.SegmentInfos[entityResourceIndex].TargetContainer;
 
            if (entityContainer != null)
            {
                // Since this is the entity which is going to get modified, then we need to check for rights
                if (service.RequestParams.AstoriaHttpVerb == AstoriaVerbs.PUT) 
                {
                    service.Configuration.CheckResourceRights(entityContainer, EntitySetRights.WriteReplace); 
                } 
                else if (service.RequestParams.AstoriaHttpVerb == AstoriaVerbs.MERGE)
                { 
                    service.Configuration.CheckResourceRights(entityContainer, EntitySetRights.WriteMerge);
                }
                else
                { 
                    Debug.Assert(
                        service.RequestParams.AstoriaHttpVerb == AstoriaVerbs.POST || 
                        service.RequestParams.AstoriaHttpVerb == AstoriaVerbs.DELETE, 
                        "expecting POST and DELETE methods");
                    service.Configuration.CheckResourceRights(entityContainer, EntitySetRights.WriteMerge | EntitySetRights.WriteReplace); 
                }
            }

            entityResource = service.GetResource(description, entityResourceIndex, null); 
            if (entityResource == null)
            { 
                throw DataServiceException.CreateBadRequestError(Strings.BadRequest_DereferencingNullPropertyValue(description.SegmentInfos[entityResourceIndex].Identifier)); 
            }
 
            // now walk from the entity resource to the resource to modify.
            // for open types, as you walk, if the intermediate resource is an entity,
            // update the entityResource accordingly.
            object resourceToModify = entityResource; 
            for (int i = entityResourceIndex + 1; i <= modifyingResourceIndex; i++)
            { 
                resourceToModify = service.Provider.GetValue(resourceToModify, description.SegmentInfos[i].Identifier); 

#if ASTORIA_OPEN_OBJECT 
                if (description.TargetKind == RequestTargetKind.OpenProperty ||
                    description.TargetKind == RequestTargetKind.OpenPropertyValue)
                {
                    object resource = service.Provider.ResolveResource(resourceToModify); 
                    if (resource == null)
                    { 
                        throw DataServiceException.CreateBadRequestError(Strings.BadRequest_DereferencingNullPropertyValue(description.SegmentInfos[i].Identifier)); 
                    }
 
                    ResourceType resourceType = service.Provider.GetResourceType(resource.GetType());
                    if (resourceType == null)
                    {
                        throw new InvalidOperationException(Strings.BadProvider_InvalidTypeSpecified(resource.GetType().FullName)); 
                    }
 
                    if (resourceType.ResourceTypeKind == ResourceTypeKind.EntityType) 
                    {
                        entityResource = resource; 
                        entityContainer = service.Provider.GetContainerForResourceType(resourceType.Type);
                    }
                }
#endif 
            }
 
#if ASTORIA_OPEN_OBJECT 
            if (entityContainer == null)
            { 
                Debug.Assert(
                    description.TargetKind == RequestTargetKind.OpenProperty ||
                    description.TargetKind == RequestTargetKind.OpenPropertyValue,
                    "its a open property target. Hence resource container must be null"); 
                object resource = service.Provider.ResolveResource(entityResource);
                entityContainer = service.Provider.GetContainerForResourceType(resource.GetType()); 
            } 
#else
            Debug.Assert(entityContainer != null, "entityContainer != null"); 
#endif

            return resourceToModify;
        } 

        ///  
        /// Modify the value of the given resource to the given value 
        /// 
        /// description about the request 
        /// resource that needs to be modified
        /// the new value for the target resource
        /// specifies the content format of the payload
        /// provider for this request 
        internal static void ModifyResource(RequestDescription description, object resourceToBeModified, object requestValue, ContentFormat contentFormat, IDataServiceProvider provider)
        { 
#if ASTORIA_OPEN_OBJECT 
            if ((description.TargetKind == RequestTargetKind.OpenProperty && !description.LastSegmentInfo.HasKeyValues) ||
                description.TargetKind == RequestTargetKind.OpenPropertyValue) 
            {
                SetOpenPropertyValue(resourceToBeModified, description.ContainerName, requestValue, provider);
            }
            else 
#endif
            { 
                Debug.Assert( 
                    description.TargetKind == RequestTargetKind.Primitive ||
                    description.TargetKind == RequestTargetKind.ComplexObject || 
                    description.TargetKind == RequestTargetKind.PrimitiveValue,
                    "unexpected target kind encountered");

                // update the primitive value 
                ResourceProperty propertyToUpdate = description.LastSegmentInfo.ProjectedProperty;
                SetPropertyValue(propertyToUpdate, resourceToBeModified, requestValue, contentFormat, provider); 
            } 
        }
 
        /// 
        /// Get the resource referred by the given segment
        /// 
        /// information about the segment. 
        /// full name of the resource referred by the segment.
        /// data service type to which the request was made 
        /// whether to check if the resource is null or not. 
        /// returns the resource returned by the provider.
        internal static object GetResource(SegmentInfo segmentInfo, string fullTypeName, IDataService service, bool checkForNull) 
        {
            if (segmentInfo.TargetContainer != null)
            {
#if ASTORIA_OPEN_OBJECT 
                Debug.Assert(
                    segmentInfo.TargetKind != RequestTargetKind.OpenProperty && 
                    segmentInfo.TargetKind != RequestTargetKind.OpenPropertyValue, 
                    "container can be null only for open types");
#endif 

                service.Configuration.CheckResourceRights(segmentInfo.TargetContainer, EntitySetRights.ReadSingle);
            }
 
            object resource = service.Provider.GetResource((IQueryable)segmentInfo.RequestEnumerable, fullTypeName);
            if (resource == null && 
                (segmentInfo.HasKeyValues || checkForNull)) 
            {
                throw DataServiceException.CreateResourceNotFound(segmentInfo.Identifier); 
            }

            return resource;
        } 

        ///  
        /// Deserializes the given stream into clr object as specified in the payload 
        /// 
        /// description about the target request 
        /// the object instance that it created and populated from the reader
        internal object ReadEntity(RequestDescription requestDescription)
        {
            // If the description points to a resource, 
            // we need to materialize the object and return back.
            SegmentInfo segmentInfo = requestDescription.LastSegmentInfo; 
            if (!this.Update) 
            {
                Debug.Assert(!segmentInfo.SingleResult, "POST operation is allowed only on collections"); 
                SegmentInfo adjustedSegment = new SegmentInfo();
                adjustedSegment.TargetKind = segmentInfo.TargetKind;
                adjustedSegment.TargetSource = segmentInfo.TargetSource;
                adjustedSegment.SingleResult = true; 
                adjustedSegment.ProjectedProperty = segmentInfo.ProjectedProperty;
                adjustedSegment.TargetElementType = segmentInfo.TargetElementType; 
                adjustedSegment.TargetContainer = segmentInfo.TargetContainer; 
                adjustedSegment.Identifier = segmentInfo.Identifier;
                segmentInfo = adjustedSegment; 
            }

            return this.CreateSingleObject(segmentInfo);
        } 

        ///  
        /// Handle bind operation 
        /// 
        /// information about the request uri. 
        /// the child resource which needs to be linked.
        /// returns the parent object to which an new object was linked to.
        internal object HandleBindOperation(RequestDescription description, object linkResource)
        { 
            Debug.Assert(description != null, "description != null");
 
            object entityGettingModified; 
            ResourceContainer container;
 
            object resourceToBeModified = Deserializer.GetResourceToModify(description, this.Service, true /*allowCrossReference*/, out entityGettingModified, out container);
            Debug.Assert(resourceToBeModified == entityGettingModified, "Since this is a link operation, modifying resource must be the entity resource");

#if ASTORIA_OPEN_OBJECT 
            if (description.TargetKind != RequestTargetKind.OpenProperty)
#endif 
            { 
                this.tracker.TrackAction(entityGettingModified, container, UpdateOperations.Change);
            } 

            if (description.IsSingleResult)
            {
                this.Service.Provider.SetReference(entityGettingModified, description.ContainerName, linkResource); 
            }
            else 
            { 
                this.Service.Provider.AddReferenceToCollection(entityGettingModified, description.ContainerName, linkResource);
            } 

            return entityGettingModified;
        }
 
        /// 
        /// Handles post request. 
        ///  
        /// description about the uri for the post operation.
        /// returns the resource that is getting inserted or binded - as specified in the payload. 
        internal object HandlePostRequest(RequestDescription requestDescription)
        {
            Debug.Assert(!this.Update, "This method must be called for POST operations only");
            Debug.Assert(requestDescription != null, "requestDescription != null"); 
            object resourceInPayload;
 
            if (requestDescription.LinkUri) 
            {
                string uri = this.GetLinkUriFromPayload(); 
                resourceInPayload = this.CreateObjectFromUri(null, null, uri, false/*verifyETag*/, true /*checkForNull*/);
                Debug.Assert(resourceInPayload != null, "link resource cannot be null");
                this.HandleBindOperation(requestDescription, resourceInPayload);
            } 
            else
            { 
                if (requestDescription.LastSegmentInfo.TargetContainer != null) 
                {
                    this.Service.Configuration.CheckResourceRights(requestDescription.LastSegmentInfo.TargetContainer, EntitySetRights.WriteAppend); 
                }

                resourceInPayload = this.ReadEntity(requestDescription);
                if (requestDescription.TargetSource == RequestTargetSource.Property) 
                {
                    Debug.Assert( 
#if ASTORIA_OPEN_OBJECT 
                        (requestDescription.TargetKind == RequestTargetKind.OpenProperty && !requestDescription.IsSingleResult) ||
#endif 
                        requestDescription.Property.Kind == ResourcePropertyKind.ResourceSetReference,
                        "Expecting POST resource set property");
                    this.HandleBindOperation(requestDescription, resourceInPayload);
                } 
                else
                { 
                    Debug.Assert(requestDescription.TargetSource == RequestTargetSource.EntitySet, "Expecting POST on entity set"); 
                    this.tracker.TrackAction(resourceInPayload, requestDescription.LastSegmentInfo.TargetContainer, UpdateOperations.Add);
                } 
            }

            return resourceInPayload;
        } 

        ///  
        /// Update the object count value to the given value. 
        /// 
        /// value to be set for object count. 
        internal void UpdateObjectCount(int value)
        {
            Debug.Assert(0 <= value, "MaxObjectCount cannot be initialized to a negative number");
            Debug.Assert(value <= this.Service.Configuration.MaxObjectCountOnInsert, "On initialize, the value should be less than max object count"); 
            this.objectCount = value;
        } 
 
        /// Creates a new SegmentInfo for the specified .
        /// Property to create segment info for (possibly null). 
        /// Name for the property.
        /// Whether a single result is expected.
        /// 
        /// A new  instance that describes the specfied  
        /// as a target, or an open proprty if  is null.
        ///  
        protected static SegmentInfo CreateSegment(ResourceProperty property, string propertyName, bool singleResult) 
        {
            SegmentInfo result = new SegmentInfo(); 
            result.TargetSource = RequestTargetSource.Property;
            result.SingleResult = singleResult;
            result.Identifier = propertyName;
#if ASTORIA_OPEN_OBJECT 
            if (property == null)
            { 
                result.TargetKind = RequestTargetKind.OpenProperty; 
            }
            else 
#else
            Debug.Assert(property != null, "property != null");
#endif
            { 
                result.TargetKind = RequestTargetKind.Resource;
                result.Identifier = propertyName; 
                result.ProjectedProperty = property; 
                result.TargetElementType = property.ResourceClrType;
                result.TargetContainer = property.ResourceContainer; 
            }

            return result;
        } 

        ///  
        /// Set the value of the given resource property to the new value 
        /// 
        /// property whose value needs to be updated 
        /// instance of the declaring type of the property for which the property value needs to be updated
        /// new value for the property
        /// specifies the content format of the payload
        /// provider for this request 
        protected static void SetPropertyValue(ResourceProperty resourceProperty, object declaringResource, object propertyValue, ContentFormat contentFormat, IDataServiceProvider provider)
        { 
            Debug.Assert( 
                resourceProperty.TypeKind == ResourceTypeKind.ComplexType ||
                resourceProperty.TypeKind == ResourceTypeKind.Primitive, 
                "Only primitive and complex type values must be set via this method");

            // For open types, resource property can be null
            if (resourceProperty.TypeKind == ResourceTypeKind.Primitive) 
            {
                // First convert the value of the property to the expected type, as specified in the resource property 
                propertyValue = ConvertValues(propertyValue, resourceProperty, contentFormat); 
            }
 
            provider.SetValue(declaringResource, resourceProperty.Name, propertyValue);
        }

#if ASTORIA_OPEN_OBJECT 

        ///  
        /// Set the value of the open property 
        /// 
        /// instance of the declaring type of the property for which the property value needs to be updated 
        /// name of the open property to update
        /// new value for the property
        /// provider for this request
        protected static void SetOpenPropertyValue(object declaringResource, string propertyName, object propertyValue, IDataServiceProvider provider) 
        {
            provider.SetValue(declaringResource, propertyName, propertyValue); 
        } 

#endif 

        /// 
        /// Reads the content from the stream reader and returns it as string
        ///  
        /// stream reader from which the content needs to be read
        /// string containing the content as read from the stream reader 
        protected static string ReadStringFromStream(StreamReader streamReader) 
        {
            return streamReader.ReadToEnd(); 
        }

        /// 
        /// Make sure binding operations cannot be performed in PUT operations 
        /// 
        /// http method name for the request. 
        protected static void CheckForBindingInPutOperations(AstoriaVerbs requestVerb) 
        {
            // Cannot bind in PUT operations. 
            if (requestVerb == AstoriaVerbs.PUT)
            {
                throw DataServiceException.CreateBadRequestError(Strings.DataServiceException_GeneralError);
            } 
        }
 
        ///  
        /// Create the object from the given payload and return the top level object
        ///  
        /// info about the object being created
        /// instance of the object created
        protected abstract object CreateSingleObject(SegmentInfo segmentInfo);
 
        /// 
        /// Get the resource referred by the uri in the payload 
        ///  
        /// resource referred by the uri in the payload.
        protected abstract string GetLinkUriFromPayload(); 

        /// Provides an opportunity to clean-up resources.
        /// 
        /// Whether the call is being made from an explicit call to 
        /// IDisposable.Dispose() rather than through the finalizer.
        ///  
        protected virtual void Dispose(bool disposing) 
        {
        } 

        /// Marks the fact that a recursive method was entered, and checks that the depth is allowed.
        protected void RecurseEnter()
        { 
            this.recursionDepth++;
            Debug.Assert(this.recursionDepth <= RecursionLimit, "this.recursionDepth <= recursionLimit"); 
            if (this.recursionDepth == RecursionLimit) 
            {
                // 400 - Bad Request 
                throw DataServiceException.CreateDeepRecursion(RecursionLimit);
            }
        }
 
        /// Marks the fact that a recursive method is leaving..
        protected void RecurseLeave() 
        { 
            this.recursionDepth--;
            Debug.Assert(0 <= this.recursionDepth, "0 <= this.recursionDepth"); 
            Debug.Assert(this.recursionDepth < RecursionLimit, "this.recursionDepth < recursionLimit");
        }

        ///  
        /// Create a new instance of the given resource type and populate the key properties from the segment info
        ///  
        /// resource type whose instance needs to be created 
        /// segment info containing the description of the uri
        /// uri which contains the key for the given resource type 
        /// verify etag value of the current resource with one specified in the request header
        /// validate that the resource cannot be null.
        /// a new instance of the given resource type with key values populated
        protected object CreateObjectFromUri( 
            ResourceType resourceType,
            SegmentInfo segmentInfo, 
            string uri, 
            bool verifyETag,
            bool checkForNull) 
        {
            RequestDescription description = null;
            if (uri != null)
            { 
                Uri referencedUri = RequestUriProcessor.GetAbsoluteUriFromReference(uri, this.Service.RequestParams);
                description = RequestUriProcessor.ProcessRequestUri(referencedUri, this.Service); 
            } 

            return this.CreateObjectFromUri(resourceType, segmentInfo, description, verifyETag, checkForNull, false /*replaceResource*/); 
        }

        /// 
        /// Create a new instance of the given resource type and populate the key properties from the segment info 
        /// 
        /// resource type whose instance needs to be created 
        /// segment info containing the description of the uri 
        /// information about the uri in the payload.
        /// verify etag value of the current resource with one specified in the request header 
        /// validate that the resource cannot be null.
        /// reset the resource as referred by the segment.
        /// a new instance of the given resource type with key values populated
        protected object CreateObjectFromUri( 
            ResourceType resourceType,
            SegmentInfo segmentInfo, 
            RequestDescription description, 
            bool verifyETag,
            bool checkForNull, 
            bool replaceResource)
        {
            Debug.Assert(resourceType == null && !verifyETag || resourceType != null, "For etag verification, resource type must be specified");
 
            if (description != null)
            { 
                segmentInfo = description.LastSegmentInfo; 
            }
            else if (segmentInfo.RequestEnumerable == null) 
            {
                throw DataServiceException.CreateBadRequestError(Strings.BadRequest_ResourceCanBeCrossReferencedOnlyForBindOperation);
            }
 
            ResourceContainer container = segmentInfo.TargetContainer;
 
            object resourceCookie; 
            string typeName = resourceType != null ? resourceType.FullName : null;
            string containerName = container != null ? container.Name : null; 

            if (description != null)
            {
                resourceCookie = this.Service.GetResource(description, description.SegmentInfos.Length - 1, typeName); 
                if (checkForNull)
                { 
                    WebUtil.CheckResourceExists(resourceCookie != null, description.LastSegmentInfo.Identifier); 
                }
            } 
            else
            {
                resourceCookie = Deserializer.GetResource(segmentInfo, typeName, this.Service, checkForNull);
            } 

            if (resourceType != null) 
            { 
                ICollection etagProperties = this.Service.Provider.GetETagProperties(containerName, resourceType.Type);
                if (etagProperties.Count == 0) 
                {
                    if (this.service.RequestParams.IfMatch != null)
                    {
                        throw DataServiceException.CreateBadRequestError(Strings.Serializer_NoETagPropertiesForType); 
                    }
                } 
                else if (verifyETag) 
                {
                    Debug.Assert(checkForNull, "for verifying etags, the resource must not be null"); 
                    if (this.service.RequestParams.IfMatch == null)
                    {
                        throw DataServiceException.CreateBadRequestError(Strings.DataService_CannotPerformPutOperationWithoutETag(resourceType.FullName));
                    } 

                    if (this.service.RequestParams.IfMatch != XmlConstants.HttpAnyETag) 
                    { 
                        string currentETagValue = WebUtil.GetETagValue(resourceCookie, etagProperties, this.Service.Provider);
                        if (currentETagValue != this.service.RequestParams.IfMatch) 
                        {
                            throw DataServiceException.CreatePreConditionFailedError(Strings.Serializer_ETagValueDoesNotMatch);
                        }
                    } 
                }
            } 
 
            if (replaceResource)
            { 
                Debug.Assert(checkForNull, "For resetting resource, the value cannot be null");
                resourceCookie = this.Service.Provider.ResetResource(resourceCookie);
                WebUtil.CheckResourceExists(resourceCookie != null, segmentInfo.Identifier);
            } 

            return resourceCookie; 
        } 

        ///  
        /// Check and increment the object count
        /// 
        protected void CheckAndIncrementObjectCount()
        { 
            Debug.Assert(this.Update && this.objectCount == 0 || !this.Update, "For updates, the object count is never tracked");
            Debug.Assert(this.objectCount <= this.Service.Configuration.MaxObjectCountOnInsert, "The object count should never exceed the limit"); 
 
            if (!this.Update)
            { 
                this.objectCount++;

                if (this.objectCount > this.Service.Configuration.MaxObjectCountOnInsert)
                { 
                    throw new DataServiceException(413, Strings.DataServiceException_GeneralError);
                } 
            } 
        }
 
        /// 
        /// Read the byte from the given input request stream
        /// 
        /// input/request stream from which data needs to be read 
        /// byte array containing all the data read
        private static byte[] ReadByteStream(Stream stream) 
        { 
            byte[] data;
 
            // try to read data from the stream 1k at a time
            long numberOfBytesRead = 0;
            int result = 0;
            List byteData = new List(); 

            do 
            { 
                data = new byte[4000];
                result = stream.Read(data, 0, data.Length); 
                numberOfBytesRead += result;
                byteData.Add(data);
            }
            while (result == data.Length); 

            // Find out the total number of bytes read and copy data from byteData to data 
            data = new byte[numberOfBytesRead]; 
            for (int i = 0; i < byteData.Count - 1; i++)
            { 
                Buffer.BlockCopy(byteData[i], 0, data, i * 4000, 4000);
            }

            // For the last thing, copy the remaining number of bytes, not always 4000 
            Buffer.BlockCopy(byteData[byteData.Count - 1], 0, data, (byteData.Count - 1) * 4000, result);
            return data; 
        } 

        ///  
        /// Returns true if we need to query the provider before updating.
        /// 
        /// request description
        /// value corresponding to the payload for this request 
        /// provider against which the request was targeted
        /// returns true if we need to issue an query to satishfy the request 
#if !ASTORIA_OPEN_OBJECT 
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "requestValue", Justification = "Required for open type support.")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "provider", Justification = "Required for open type support.")] 
#endif
        private static bool IsQueryRequired(RequestDescription requestDescription, object requestValue, IDataServiceProvider provider)
        {
            Debug.Assert(requestDescription.IsSingleResult, "requestDescription.IsSingleResult"); 

            if (requestDescription.TargetKind == RequestTargetKind.PrimitiveValue || 
                requestDescription.TargetKind == RequestTargetKind.Primitive || 
#if ASTORIA_OPEN_OBJECT
                requestDescription.TargetKind == RequestTargetKind.OpenPropertyValue || 
#endif
                requestDescription.TargetKind == RequestTargetKind.ComplexObject)
            {
                return true; 
            }
 
#if ASTORIA_OPEN_OBJECT 
            if (requestDescription.TargetKind == RequestTargetKind.OpenProperty &&
                !requestDescription.LastSegmentInfo.HasKeyValues) 
            {
                // if the value is null, then just set it, since we don't know the type
                if (requestValue == null || WebUtil.IsPrimitiveType(requestValue.GetType()))
                { 
                    return true;
                } 
 
                // otherwise just set the complex type properties
                if (provider.GetResourceTypeKind(requestValue.GetType()) == ResourceTypeKind.ComplexType) 
                {
                    return true;
                }
            } 
#endif
 
            return false; 
        }
    } 
}

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