Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / ndp / fx / src / DataWeb / Server / System / Data / Services / Serializers / Deserializer.cs / 1 / 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 /// 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; } ///for the specified stream. Initializes a new /// 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; } ///based on a different one. 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 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; } ///instance that describes the specfied /// as a target, or an open proprty if is null. /// /// 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) { ICollectionetagProperties = 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; ListbyteData = 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 /// 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; } ///for the specified stream. Initializes a new /// 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; } ///based on a different one. 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 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; } ///instance that describes the specfied /// as a target, or an open proprty if is null. /// /// 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) { ICollectionetagProperties = 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; ListbyteData = 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

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- EmbeddedMailObjectsCollection.cs
- ExpressionsCollectionConverter.cs
- TargetConverter.cs
- ModuleBuilder.cs
- TransportDefaults.cs
- ProbeMatches11.cs
- SystemTcpConnection.cs
- UidPropertyAttribute.cs
- ForceCopyBuildProvider.cs
- PrintingPermissionAttribute.cs
- RootAction.cs
- ErrorRuntimeConfig.cs
- TextStore.cs
- VisualProxy.cs
- AccessControlList.cs
- RawStylusActions.cs
- IconBitmapDecoder.cs
- ToolStripSeparator.cs
- PageContent.cs
- WizardPanelChangingEventArgs.cs
- _LocalDataStoreMgr.cs
- HScrollBar.cs
- XmlSerializerSection.cs
- ExpressionWriter.cs
- TabControlAutomationPeer.cs
- ConnectionProviderAttribute.cs
- Pointer.cs
- TimelineCollection.cs
- FlowDocumentPaginator.cs
- AnonymousIdentificationSection.cs
- XsdBuildProvider.cs
- LockedActivityGlyph.cs
- AnimationException.cs
- TableCell.cs
- Content.cs
- CodeCatchClause.cs
- filewebresponse.cs
- SingleConverter.cs
- CheckBox.cs
- PathFigure.cs
- ImageDrawing.cs
- XmlAnyAttributeAttribute.cs
- GenericEnumConverter.cs
- CustomValidator.cs
- XsltArgumentList.cs
- NoResizeHandleGlyph.cs
- COM2ExtendedTypeConverter.cs
- CommentEmitter.cs
- DataSourceSelectArguments.cs
- InstancePersistenceCommandException.cs
- AuthorizationRuleCollection.cs
- UTF7Encoding.cs
- TableItemStyle.cs
- ConfigurationFileMap.cs
- DynamicRendererThreadManager.cs
- DataGridColumnCollection.cs
- clipboard.cs
- Control.cs
- DocumentStatusResources.cs
- TextPointer.cs
- ElementProxy.cs
- EntityDataSource.cs
- SamlConstants.cs
- Token.cs
- ReflectionTypeLoadException.cs
- CfgParser.cs
- DbConnectionOptions.cs
- ObjectConverter.cs
- MonitoringDescriptionAttribute.cs
- LocalizationParserHooks.cs
- SweepDirectionValidation.cs
- ProfileInfo.cs
- CodeAttachEventStatement.cs
- NamedPipeChannelFactory.cs
- ArithmeticException.cs
- ContactManager.cs
- TextBoxBase.cs
- PropertyDescriptorComparer.cs
- AuthenticationSection.cs
- CodeDefaultValueExpression.cs
- CollectionViewGroupRoot.cs
- TrackingAnnotationCollection.cs
- CreatingCookieEventArgs.cs
- HttpConfigurationSystem.cs
- CallbackValidator.cs
- HeaderLabel.cs
- Utilities.cs
- LinqDataSource.cs
- SettingsAttributeDictionary.cs
- NavigationService.cs
- HostUtils.cs
- XmlDownloadManager.cs
- SafeNativeMemoryHandle.cs
- TiffBitmapEncoder.cs
- ConnectionPoolRegistry.cs
- TransportConfigurationTypeElementCollection.cs
- BStrWrapper.cs
- RepeatBehavior.cs
- SHA1.cs
- ToolboxItemCollection.cs