Deserializer.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / DataWeb / Server / System / Data / Services / Serializers / Deserializer.cs / 1305376 / 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;
 
        /// Request description for the top level target entity.
        private RequestDescription description;

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

        /// Return the IUpdatable object to use to make changes to entity states
        protected UpdatableWrapper Updatable 
        {
            get 
            { 
                return this.Service.Updatable;
            } 
        }

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

        /// Request description for the top level target entity. 
        protected RequestDescription RequestDescription
        {
            get { return this.description; }
        } 

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

        ///  
        /// Creates a new  for the specified stream.
        ///  
        /// description about the request uri. 
        /// Data service for which the deserializer will act.
        /// indicates whether this is a update operation or not 
        /// Tracker to use for modifications.
        /// A new instance of .
        internal static Deserializer CreateDeserializer(RequestDescription description, IDataService dataService, bool update, UpdateTracker tracker)
        { 
            string mimeType;
            System.Text.Encoding encoding; 
            DataServiceHostWrapper host = dataService.OperationContext.Host; 
            HttpProcessUtility.ReadContentType(host.RequestContentType, out mimeType, out encoding);
            ContentFormat requestFormat = WebUtil.SelectRequestFormat(mimeType, description); 
            Stream requestStream = host.RequestStream;
            Debug.Assert(requestStream != null, "requestStream != null");
            Debug.Assert(tracker != null, "Change tracker must always be created.");
            Deserializer deserializer = null; 

            Debug.Assert( 
                (!update /*POST*/ && dataService.OperationContext.Host.AstoriaHttpVerb == AstoriaVerbs.POST) || 
                (update /*PUT,MERGE*/ && (dataService.OperationContext.Host.AstoriaHttpVerb == AstoriaVerbs.MERGE || dataService.OperationContext.Host.AstoriaHttpVerb == AstoriaVerbs.PUT)),
                "For PUT and MERGE, update must be true; for POST, update must be false"); 

            switch (requestFormat)
            {
                case ContentFormat.Json: 
                    deserializer = new JsonDeserializer(
                        requestStream, 
                        encoding, 
                        update,
                        dataService, 
                        tracker);
                    break;
                case ContentFormat.Atom:
                    SyndicationFormatterFactory factory = new Atom10FormatterFactory(); 
                    deserializer = new SyndicationDeserializer(
                        requestStream,  // stream 
                        encoding,       // encoding 
                        dataService,    // dataService
                        update, 
                        factory,
                        tracker);       // factory
                    break;
                case ContentFormat.PlainXml: 
                    deserializer = new PlainXmlDeserializer(
                        requestStream, 
                        encoding, 
                        dataService,
                        update, 
                        tracker);
                    break;
                default:
                    throw new DataServiceException(415, Strings.BadRequest_UnsupportedRequestContentType(host.RequestContentType)); 
            }
 
            Debug.Assert(deserializer != null, "deserializer != null"); 

            return deserializer; 
        }

        /// 
        /// 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
        /// underlying data service provider. 
        /// object which is in [....] with the properties type
        internal static object ConvertValues(object value, ResourceProperty property, ContentFormat contentFormat, DataServiceProviderWrapper provider)
        {
            Debug.Assert(property.TypeKind == ResourceTypeKind.Primitive, "This method must be called for primitive types only"); 

            if (contentFormat == ContentFormat.Json) 
            { 
                return JsonDeserializer.ConvertValues(value, property.Name, property.Type, provider);
            } 
            else if (contentFormat == ContentFormat.Atom || contentFormat == ContentFormat.PlainXml)
            {
                return PlainXmlDeserializer.ConvertValuesForXml(value, property.Name, property.Type);
            } 
            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;
            object entityGettingModified = null;
            ResourceSetWrapper container = null; 
            string mimeType;
            Encoding encoding; 
            DataServiceHostWrapper host = dataService.OperationContext.Host; 
            HttpProcessUtility.ReadContentType(host.RequestContentType, out mimeType, out encoding);
 
            UpdateTracker tracker = UpdateTracker.CreateUpdateTracker(dataService);
            Debug.Assert(tracker != null, "Change tracker must always be created.");

            // 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 (description.TargetKind == RequestTargetKind.OpenPropertyValue || 
                description.TargetKind == RequestTargetKind.PrimitiveValue) 
            {
                string contentType; 
                requestFormat = WebUtil.GetResponseFormatForPrimitiveValue(description.TargetResourceType, out contentType);
                if (!WebUtil.CompareMimeType(contentType, mimeType))
                {
                    if (description.TargetResourceType != null) 
                    {
                        throw new DataServiceException(415, Strings.BadRequest_InvalidContentType(host.RequestContentType, description.TargetResourceType.Name)); 
                    } 
                    else
                    { 
                        throw new DataServiceException(415, Strings.BadRequest_InvalidContentTypeForOpenProperty(host.RequestContentType, description.ContainerName));
                    }
                }
 
                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 if (description.TargetKind == RequestTargetKind.MediaResource) 
            {
                requestFormat = ContentFormat.Binary; 
                requestValue = stream;
            }
            else
            { 
                requestFormat = WebUtil.SelectRequestFormat(mimeType, description);
                using (Deserializer deserializer = Deserializer.CreateDeserializer(description, dataService, true /*update*/, tracker)) 
                { 
                    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.GetTargetResourceToBind(uri, true /*checkNull*/); 
                        entityGettingModified = Deserializer.HandleBindOperation(description, linkResource, deserializer.Service, deserializer.Tracker); 
                        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)); 
                        }
                    }
                }
            } 

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

                tracker.TrackAction(entityGettingModified, container, UpdateOperations.Change); 

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

            tracker.FireNotifications(); 

            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. 
        /// 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, out object entityResource, out ResourceSetWrapper container)
        {
            return GetResourceToModify(description, service, false /*allowCrossReference*/, out entityResource, out container, false /*checkETag*/);
        } 

        ///  
        /// 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. 
        /// whether to check the etag for the entity resource that 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 ResourceSetWrapper entityContainer,
            bool checkETag) 
        {
            Debug.Assert(description.SegmentInfos.Length >= 2, "description.SegmentInfos.Length >= 2"); 
 
            UpdatableWrapper updatable = service.Updatable;
 
            if (!allowCrossReferencing && description.RequestEnumerable == null)
            {
                throw DataServiceException.CreateBadRequestError(Strings.BadRequest_ResourceCanBeCrossReferencedOnlyForBindOperation);
            } 

            // Set the index of the modifying resource 
            int modifyingResourceIndex = -1; 
            if (
                description.TargetKind == RequestTargetKind.OpenPropertyValue || 
                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) 
            {
                DataServiceHostWrapper host = service.OperationContext.Host; 
 
                // Since this is the entity which is going to get modified, then we need to check for rights
                if (host.AstoriaHttpVerb == AstoriaVerbs.PUT) 
                {
                    DataServiceConfiguration.CheckResourceRights(entityContainer, EntitySetRights.WriteReplace);
                }
                else if (host.AstoriaHttpVerb == AstoriaVerbs.MERGE) 
                {
                    DataServiceConfiguration.CheckResourceRights(entityContainer, EntitySetRights.WriteMerge); 
                } 
                else
                { 
                    Debug.Assert(
                        host.AstoriaHttpVerb == AstoriaVerbs.POST ||
                        host.AstoriaHttpVerb == AstoriaVerbs.DELETE,
                        "expecting POST and DELETE methods"); 
                    DataServiceConfiguration.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 = updatable.GetValue(resourceToModify, description.SegmentInfos[i].Identifier);
            } 

            if (entityContainer == null) 
            { 
                Debug.Assert(
                    description.TargetKind == RequestTargetKind.OpenProperty || 
                    description.TargetKind == RequestTargetKind.OpenPropertyValue,
                    "its a open property target. Hence resource set must be null");

                // Open navigation properties are not supported on OpenTypes. 
                throw DataServiceException.CreateBadRequestError(Strings.OpenNavigationPropertiesNotSupportedOnOpenTypes(description.SegmentInfos[entityResourceIndex].Identifier));
            } 
 
            // If checkETag is true, then we need to check the etag for the resource
            // Note that MediaResource has a separate etag, we don't need to check the MLE etag if the target kind is MediaResource 
            if (checkETag && !Deserializer.IsCrossReferencedSegment(description.SegmentInfos[modifyingResourceIndex], service) && description.TargetKind != RequestTargetKind.MediaResource)
            {
                service.Updatable.SetETagValues(entityResource, entityContainer);
            } 

            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 
        /// Service this request is against 
        internal static void ModifyResource(RequestDescription description, object resourceToBeModified, object requestValue, ContentFormat contentFormat, IDataService service)
        { 
            if (description.TargetKind == RequestTargetKind.OpenProperty ||
                description.TargetKind == RequestTargetKind.OpenPropertyValue)
            {
                Debug.Assert(!description.LastSegmentInfo.HasKeyValues, "CreateSegments must have caught the problem already."); 
                SetOpenPropertyValue(resourceToBeModified, description.ContainerName, requestValue, service);
            } 
            else if (description.TargetKind == RequestTargetKind.MediaResource) 
            {
                SetStreamPropertyValue(resourceToBeModified, (Stream)requestValue, service, description); 
            }
            else
            {
                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, service);
            } 
        }
 
        ///  
        /// 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)
            { 
                Debug.Assert(
                    segmentInfo.TargetKind != RequestTargetKind.OpenProperty &&
                    segmentInfo.TargetKind != RequestTargetKind.OpenPropertyValue,
                    "container can be null only for open types"); 

                DataServiceConfiguration.CheckResourceRights(segmentInfo.TargetContainer, EntitySetRights.ReadSingle); 
            } 

            object resource = service.Updatable.GetResource((IQueryable)segmentInfo.RequestEnumerable, fullTypeName); 
            if (resource == null &&
                (segmentInfo.HasKeyValues || checkForNull))
            {
                throw DataServiceException.CreateResourceNotFound(segmentInfo.Identifier); 
            }
 
            return resource; 
        }
 
        /// 
        /// Creates a Media Link Entry.
        /// 
        /// Full type name for the MLE to be created. 
        /// Request stream from the host.
        /// Service this request is against. 
        /// Description of the target request. 
        /// Update tracker instance to fire change interceptor calls
        /// Newly created Media Link Entry. 
        internal static object CreateMediaLinkEntry(string fullTypeName, Stream requestStream, IDataService service, RequestDescription description, UpdateTracker tracker)
        {
            Debug.Assert(!string.IsNullOrEmpty(fullTypeName), "!string.IsNullOrEmpty(fullTypeName)");
            Debug.Assert(requestStream != null, "requestStream != null"); 
            Debug.Assert(service != null, "service != null");
            Debug.Assert(description != null, "description != null"); 
            Debug.Assert(tracker != null, "tracker != null"); 

            object entity = service.Updatable.CreateResource(description.LastSegmentInfo.TargetContainer.Name, fullTypeName); 
            tracker.TrackAction(entity, description.LastSegmentInfo.TargetContainer, UpdateOperations.Add);
            SetStreamPropertyValue(entity, requestStream, service, description);
            return entity;
        } 

        ///  
        /// Copy the contents of the request stream into the default stream of the specified entity. 
        /// 
        /// Entity with the associated stream which we will write to. 
        /// Request stream from the host
        /// Service this is request is against
        /// Description of the target request.
        internal static void SetStreamPropertyValue(object resourceToBeModified, Stream requestStream, IDataService service, RequestDescription description) 
        {
            Debug.Assert(resourceToBeModified != null, "resourceToBeModified != null"); 
            Debug.Assert(requestStream.CanRead, "requestStream.CanRead"); 
            Debug.Assert(service != null, "service != null");
 
            resourceToBeModified = service.Updatable.ResolveResource(resourceToBeModified);

            ResourceType resourceType = service.Provider.GetResourceType(resourceToBeModified);
            if (!resourceType.IsMediaLinkEntry) 
            {
                throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidUriForMediaResource(service.OperationContext.AbsoluteRequestUri)); 
            } 

            if (service.OperationContext.Host.AstoriaHttpVerb == AstoriaVerbs.MERGE) 
            {
                throw DataServiceException.CreateMethodNotAllowed(
                    Strings.BadRequest_InvalidUriForMergeOperation(service.OperationContext.AbsoluteRequestUri),
                    DataServiceConfiguration.GetAllowedMethods(service.Configuration, description)); 
            }
 
            using (Stream writeStream = service.StreamProvider.GetWriteStream(resourceToBeModified, service.OperationContext)) 
            {
                WebUtil.CopyStream(requestStream, writeStream, service.StreamProvider.StreamBufferSize); 
            }
        }

        ///  
        /// Gets the resource from the segment enumerable.
        ///  
        /// segment from which resource needs to be returned. 
        /// returns the resource contained in the request enumerable.
        internal static object GetCrossReferencedResource(SegmentInfo segmentInfo) 
        {
            Debug.Assert(segmentInfo.RequestEnumerable != null, "The segment should always have the result");
            object[] results = (object[])segmentInfo.RequestEnumerable;
            Debug.Assert(results != null && results.Length == 1, "results != null && results.Length == 1"); 
            Debug.Assert(results[0] != null, "results[0] != null");
            return results[0]; 
        } 

        ///  
        /// Test if the given segment is a cross referenced segment in a batch operation
        /// 
        /// Segment in question
        /// service instance 
        /// True if the given segment is a cross referenced segment
        internal static bool IsCrossReferencedSegment(SegmentInfo segmentInfo, IDataService service) 
        { 
            if (segmentInfo.Identifier.StartsWith("$", StringComparison.Ordinal) && service.GetSegmentForContentId(segmentInfo.Identifier) != null)
            { 
                return true;
            }

            return false; 
        }
 
        ///  
        /// Handle bind operation
        ///  
        /// information about the request uri.
        /// the child resource which needs to be linked.
        /// data service instance
        /// update tracker instance to fire change interceptor calls 
        /// returns the parent object to which an new object was linked to.
        internal static object HandleBindOperation(RequestDescription description, object linkResource, IDataService service, UpdateTracker tracker) 
        { 
            Debug.Assert(description != null, "description != null");
            Debug.Assert(linkResource != null, "linkResource != null"); 
            Debug.Assert(service != null, "service != null");
            Debug.Assert(tracker != null, "tracker != null");

            object entityGettingModified; 
            ResourceSetWrapper container;
 
            object resourceToBeModified = Deserializer.GetResourceToModify(description, service, true /*allowCrossReference*/, out entityGettingModified, out container, false /*checkETag*/); 
            Debug.Assert(resourceToBeModified == entityGettingModified, "Since this is a link operation, modifying resource must be the entity resource");
 
            // If the container we are modifying contains any type with FF mapped KeepInContent=false properties, we need to raise the FeatureVersion.
            description.UpdateAndCheckEpmFeatureVersion(container, service);

            tracker.TrackAction(entityGettingModified, container, UpdateOperations.Change); 
            Debug.Assert(description.Property != null, "description.Property != null");
            if (description.IsSingleResult) 
            { 
                service.Updatable.SetReference(entityGettingModified, description.Property.Name, linkResource);
            } 
            else
            {
                service.Updatable.AddReferenceToCollection(entityGettingModified, description.Property.Name, linkResource);
            } 

            return entityGettingModified; 
        } 

        ///  
        /// 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)
        { 
            Debug.Assert(requestDescription != null, "requestDescription != null"); 
            this.description = requestDescription;
 
            if (requestDescription.TargetKind == RequestTargetKind.Resource)
            {
                Debug.Assert(requestDescription.LastSegmentInfo != null, "requestDescription.LastSegmentInfo != null");
                Debug.Assert(requestDescription.LastSegmentInfo.TargetContainer != null, "requestDescription.LastSegmentInfo.TargetContainer != null"); 
                Debug.Assert(requestDescription.TargetResourceType != null, "requestDescription.TargetResourceType != null");
                this.RequestDescription.UpdateAndCheckEpmFeatureVersion(this.description.LastSegmentInfo.TargetContainer, this.Service); 
            } 

            // 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.TargetResourceType = segmentInfo.TargetResourceType;
                adjustedSegment.TargetContainer = segmentInfo.TargetContainer;
                adjustedSegment.Identifier = segmentInfo.Identifier; 
                segmentInfo = adjustedSegment;
            } 
 
            return this.CreateSingleObject(segmentInfo);
        } 

        /// 
        /// 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.GetTargetResourceToBind(uri, true /*checkNull*/); 
                Debug.Assert(resourceInPayload != null, "link resource cannot be null");
                Deserializer.HandleBindOperation(requestDescription, resourceInPayload, this.Service, this.Tracker); 
            }
            else
            {
                if (requestDescription.LastSegmentInfo.TargetContainer != null) 
                {
                    DataServiceConfiguration.CheckResourceRights(requestDescription.LastSegmentInfo.TargetContainer, EntitySetRights.WriteAppend); 
                } 

                resourceInPayload = this.ReadEntity(requestDescription); 
                if (requestDescription.TargetSource == RequestTargetSource.Property)
                {
                    Debug.Assert(requestDescription.Property.Kind == ResourcePropertyKind.ResourceSetReference, "Expecting POST resource set property");
                    Deserializer.HandleBindOperation(requestDescription, resourceInPayload, this.Service, this.Tracker); 
                }
                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; 
        } 

        ///  
        /// 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 
        /// Service this is request is against 
        protected static void SetPropertyValue(ResourceProperty resourceProperty, object declaringResource, object propertyValue, ContentFormat contentFormat, IDataService service)
        { 
            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) 
            {
                // Only do the conversion if the provider explicitly asked us to do the configuration. 
                if (service.Configuration.EnableTypeConversion)
                {
                    // First convert the value of the property to the expected type, as specified in the resource property
                    propertyValue = ConvertValues(propertyValue, resourceProperty, contentFormat, service.Provider); 
                }
            } 
 
            service.Updatable.SetValue(declaringResource, resourceProperty.Name, propertyValue);
        } 

        /// 
        /// 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 
        /// Service this request is against
        protected static void SetOpenPropertyValue(object declaringResource, string propertyName, object propertyValue, IDataService service) 
        {
            service.Updatable.SetValue(declaringResource, propertyName, propertyValue);
        }
 
        /// 
        /// 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.BadRequest_CannotUpdateRelatedEntitiesInPut); 
            } 
        }
 
        /// Creates a new SegmentInfo for the specified .
        /// Property to create segment info for (possibly null).
        /// Name for the property.
        /// Target resource set 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, ResourceSetWrapper propertySet, bool singleResult)
        {
            SegmentInfo result = new SegmentInfo();
            result.TargetSource = RequestTargetSource.Property; 
            result.SingleResult = singleResult;
            result.Identifier = propertyName; 
            if (property == null) 
            {
                result.TargetKind = RequestTargetKind.OpenProperty; 
            }
            else
            {
                result.TargetKind = RequestTargetKind.Resource; 
                result.Identifier = propertyName;
                result.ProjectedProperty = property; 
                result.TargetResourceType = property.ResourceType; 
                result.TargetContainer = propertySet;
            } 

            return result;
        }
 
        /// 
        /// 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()
        {
            WebUtil.RecurseEnter(RecursionLimit, ref this.recursionDepth);
        } 

        /// Marks the fact that a recursive method is leaving. 
        protected void RecurseLeave() 
        {
            WebUtil.RecurseLeave(ref this.recursionDepth); 
        }

        /// 
        /// Returns the target/child resource to bind to an resource, which might be getting inserted or modified. 
        /// Since this is a target resource, null is a valid value here (for e.g. /Customers(1)/BestFriend value
        /// can be null) 
        ///  
        /// uri referencing to the resource to be returned.
        /// whether the resource can be null or not. 
        /// returns the resource as referenced by the uri. Throws 404 if the checkNull is true and the resource returned is null.
        protected object GetTargetResourceToBind(string uri, bool checkNull)
        {
            // 

            Uri referencedUri = RequestUriProcessor.GetAbsoluteUriFromReference(uri, this.Service.OperationContext); 
            RequestDescription requestDescription = RequestUriProcessor.ProcessRequestUri(referencedUri, this.Service); 

            // Get the resource 
            object resourceCookie = this.Service.GetResource(requestDescription, requestDescription.SegmentInfos.Length - 1, null);
            if (checkNull)
            {
                WebUtil.CheckResourceExists(resourceCookie != null, requestDescription.LastSegmentInfo.Identifier); 
            }
 
            return resourceCookie; 
        }
 
        /// 
        /// Gets a resource referenced by the given segment info.
        /// 
        /// resource type whose instance needs to be created 
        /// segment info containing the description of the uri
        /// 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 GetObjectFromSegmentInfo(
            ResourceType resourceType,
            SegmentInfo segmentInfo,
            bool verifyETag, 
            bool checkForNull,
            bool replaceResource) 
        { 
            Debug.Assert(resourceType == null && !verifyETag || resourceType != null, "For etag verification, resource type must be specified");
 
            if (segmentInfo.RequestEnumerable == null)
            {
                throw DataServiceException.CreateBadRequestError(Strings.BadRequest_ResourceCanBeCrossReferencedOnlyForBindOperation);
            } 

            object resourceCookie; 
            if (Deserializer.IsCrossReferencedSegment(segmentInfo, this.service)) 
            {
                resourceCookie = Deserializer.GetCrossReferencedResource(segmentInfo); 
            }
            else
            {
                resourceCookie = Deserializer.GetResource( 
                    segmentInfo,
                    resourceType != null ? resourceType.FullName : null, 
                    this.Service, 
                    checkForNull);
 
                // We only need to check etag if the resource is not cross-referenced. If the resource is cross-referenced,
                // there is no good way to checking etag for that resource, since it might have
                if (verifyETag)
                { 
                    this.service.Updatable.SetETagValues(resourceCookie, segmentInfo.TargetContainer);
                } 
            } 

            if (replaceResource) 
            {
                Debug.Assert(checkForNull, "For resetting resource, the value cannot be null");
                resourceCookie = this.Updatable.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.BadRequest_ExceedsMaxObjectCountOnInsert(this.Service.Configuration.MaxObjectCountOnInsert)); 
                } 
            }
        } 

        /// 
        /// Bump the minimum DSV requirement if the given resource type has friendly feed mappings with KeepInContent=false.
        /// Note that the minimum DSV requirement is type (i.e. payload) specific and is applicable for Atom format only. 
        /// 
        /// Resource type to inspect 
        /// True if resourceType is the type for the top level element in the Atom payload. 
        protected void UpdateAndCheckEpmRequestResponseDSV(ResourceType resourceType, bool topLevel)
        { 
            Debug.Assert(this.Service != null, "this.Service != null");
            Debug.Assert(resourceType != null, "Must have valid resource type");
            Debug.Assert(resourceType.ResourceTypeKind == ResourceTypeKind.EntityType, "resourceType.ResourceTypeKind == ResourceTypeKind.EntityType");
            Debug.Assert(this.RequestDescription != null, "this.RequestDescription != null"); 

            if (!resourceType.EpmIsV1Compatible) 
            { 
                // Only raise the minimum version requirement if this is an Atom request.
                if (this is SyndicationDeserializer) 
                {
                    this.RequestDescription.RaiseMinimumVersionRequirement(2, 0);
                }
 
                // For POST operations we need to raise the response version for Atom if resourceType is the type for the top
                // level element since we will serialize the newly created instance of this type in the response payload. 
                // For PUT/MEREGE we respond with 204 and therefore no backcompat issue with 1.0 
                if (!this.Update && topLevel)
                { 
                    // Friendly feeds must only bump the response version for Atom responses
                    if (WebUtil.IsAtomMimeType(this.Service.OperationContext.Host.RequestAccept))
                    {
                        this.RequestDescription.RaiseResponseVersion(2, 0); 
                    }
                } 
            } 

            WebUtil.CheckVersion(this.Service, this.RequestDescription); 
        }

        /// 
        /// 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
        private static bool IsQueryRequired(RequestDescription requestDescription, object requestValue, DataServiceProviderWrapper provider) 
        {
            Debug.Assert(requestDescription.IsSingleResult, "requestDescription.IsSingleResult");

            if (requestDescription.TargetKind == RequestTargetKind.PrimitiveValue || 
                requestDescription.TargetKind == RequestTargetKind.Primitive ||
                requestDescription.TargetKind == RequestTargetKind.OpenPropertyValue || 
                requestDescription.TargetKind == RequestTargetKind.MediaResource || 
                requestDescription.TargetKind == RequestTargetKind.ComplexObject)
            { 
                return true;
            }

            if (requestDescription.TargetKind == RequestTargetKind.OpenProperty) 
            {
                Debug.Assert(!requestDescription.LastSegmentInfo.HasKeyValues, "CreateSegments must have caught this issue."); 
 
                // 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 (WebUtil.GetNonPrimitiveResourceType(provider, requestValue).ResourceTypeKind == ResourceTypeKind.ComplexType) 
                { 
                    return true;
                } 
            }

            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