/// <summary>Initializes a new <see cref="PlainXmlDeserializer"/> based on the settings for another one.</summary> /// <param name="reader">Reader for content.</param> /// <param name="deserializer">Parent deserializer.</param> /// <param name="propertiesApplied">Properties already applied based on content</param> internal PlainXmlDeserializer(XmlReader reader, Deserializer deserializer, EpmContentDeSerializer.EpmAppliedPropertyInfo propertiesApplied) : base(deserializer) { Debug.Assert(reader != null, "reader != null"); this.xmlReader = reader; Debug.Assert(propertiesApplied != null, "Requires valid collection for applied properties"); this.propertiesApplied = propertiesApplied; this.currentPathPrefix = String.Empty; // this.xmlReaderOwned = false; }
/// <summary>Applies properties from the reader to the specified resource.</summary> /// <param name="deserializer">Deserializer which is driving the <paramref name="reader"/>.</param> /// <param name='reader'>XmlReader to read from.</param> /// <param name='resourceType'>Type of resource.</param> /// <param name='resource'>Resource to set value on.</param> /// <param name="propertiesApplied">Properties already applied based on content</param> /// <param name="currentObjectCount">current object count for this operation.</param> /// <remarks> /// This method will end as soon as it find something that is not an /// XML element to process. /// </remarks> internal static void ApplyContent(Deserializer deserializer, XmlReader reader, ResourceType resourceType, object resource, EpmContentDeSerializer.EpmAppliedPropertyInfo propertiesApplied, int currentObjectCount) { Debug.Assert(deserializer != null, "deserializer != null"); Debug.Assert(reader != null, "reader != null"); using (PlainXmlDeserializer xml = new PlainXmlDeserializer(reader, deserializer, propertiesApplied)) { // Initialize the new deserializer instance with the current object count xml.UpdateObjectCount(currentObjectCount); // load all the properties xml.ApplyContent(xml.xmlReader, resourceType, resource); // After all the properties have been loaded, initialize the current deserializer value with the object count deserializer.UpdateObjectCount(xml.MaxObjectCount); } }
/// <summary>Reads the current object from the <paramref name="item"/>.</summary> /// <param name="segmentInfo">segmentinfo containing information about the current element that is getting processes</param> /// <param name="topLevel">true if the element currently pointed by the xml reader refers to a top level element</param> /// <param name="item">Item to read from.</param> /// <returns>returns the clr object with the data populated</returns> private object CreateObject(SegmentInfo segmentInfo, bool topLevel, SyndicationItem item) { Debug.Assert(item != null, "item != null"); Debug.Assert(topLevel || !this.Update, "deep updates not supported"); this.RecurseEnter(); object result; // update the object count everytime you encounter a new resource this.CheckAndIncrementObjectCount(); // Process the type annotation. ResourceType currentResourceType = this.GetResourceType(item, segmentInfo.TargetResourceType); if (currentResourceType.ResourceTypeKind != ResourceTypeKind.EntityType) { throw DataServiceException.CreateBadRequestError(Strings.BadRequest_OnlyEntityTypesMustBeSpecifiedInEntryElement(currentResourceType.FullName)); } // We have the actual type info from the payload. Update the request/response DSV if any property is FF mapped with KeepInContent=false. this.UpdateAndCheckEpmRequestResponseDSV(currentResourceType, topLevel); // Get a resource cookie from the provider. ResourceSetWrapper container; if (segmentInfo.TargetKind == RequestTargetKind.OpenProperty) { // Open navigation properties are not supported on OpenTypes. throw DataServiceException.CreateBadRequestError(Strings.OpenNavigationPropertiesNotSupportedOnOpenTypes(segmentInfo.Identifier)); } else { Debug.Assert(segmentInfo.TargetKind == RequestTargetKind.Resource, "segmentInfo.TargetKind == RequestTargetKind.Resource"); container = segmentInfo.TargetContainer; } DataServiceHostWrapper host = this.Service.OperationContext.Host; if (this.Update) { Debug.Assert(currentResourceType.ResourceTypeKind == ResourceTypeKind.EntityType, "only expecting entity types"); // Only verify ETag if there is going to be some update applied (that's the idea) // In reality: // - for normal entities (V1 compatible) - don't check ETags if there is no content element. (Same as in V1) // - for V2 stuff - check ETags always as we can't tell if there's going to be something modified or not // with EPM properties can be anywhere in the payload and thus even without content there still can be updates // with MLE the properties are not in the content element but in their own element // It's hard to recognize if there's going to be update up front and so this below is an approximation // which seems to be good enough. Note that if we add new ways of handling properties in the content // the condition below might need to change. bool verifyETag = topLevel && (HasContent(item) || currentResourceType.HasEntityPropertyMappings || currentResourceType.IsMediaLinkEntry); bool replaceResource = topLevel && host.AstoriaHttpVerb == AstoriaVerbs.PUT; // if its a top level resource, then it cannot be null result = this.GetObjectFromSegmentInfo(currentResourceType, segmentInfo, verifyETag, topLevel /*checkForNull*/, replaceResource); if (this.Tracker != null) { this.Tracker.TrackAction(result, container, UpdateOperations.Change); } } else { if (segmentInfo.TargetKind == RequestTargetKind.Resource) { DataServiceConfiguration.CheckResourceRights(segmentInfo.TargetContainer, EntitySetRights.WriteAppend); } result = this.Updatable.CreateResource(container.Name, currentResourceType.FullName); if (this.Tracker != null) { this.Tracker.TrackAction(result, container, UpdateOperations.Add); } } // Process the content in the entry. EpmContentDeSerializer.EpmAppliedPropertyInfo propertiesApplied = new EpmContentDeSerializer.EpmAppliedPropertyInfo(); this.ApplyProperties(item, currentResourceType, propertiesApplied, result); // Perform application of epm properties here if (currentResourceType.HasEntityPropertyMappings) { new EpmContentDeSerializer(currentResourceType, result).DeSerialize( item, new EpmContentDeSerializer.EpmContentDeserializerState { IsUpdateOperation = this.Update, Updatable = this.Updatable, Service = this.Service, PropertiesApplied = propertiesApplied }); } // Process the links in the entry. foreach (SyndicationLink link in item.Links) { string navigationPropertyName = UriUtil.GetNameFromAtomLinkRelationAttribute(link.RelationshipType); if (null == navigationPropertyName) { continue; } Deserializer.CheckForBindingInPutOperations(host.AstoriaHttpVerb); Debug.Assert(segmentInfo.TargetContainer != null, "segmentInfo.TargetContainer != null"); this.ApplyLink(link, segmentInfo.TargetContainer, currentResourceType, result, navigationPropertyName); } this.RecurseLeave(); return(result); }
/// <summary>Applies the properties in the plain XML content to the specified resource.</summary> /// <param name="item">item to read from.</param> /// <param name='resourceType'>Type of resource whose values are being set.</param> /// <param name="propertiesApplied">Properties that have been applied to the <paramref name="resource"/></param> /// <param name='resource'>Target resource.</param> private void ApplyProperties(SyndicationItem item, ResourceType resourceType, EpmContentDeSerializer.EpmAppliedPropertyInfo propertiesApplied, object resource) { Debug.Assert(item != null, "item != null"); Debug.Assert(resourceType != null, "resourceType != null"); Debug.Assert(propertiesApplied != null, "propertiesApplied != null"); Debug.Assert(resource != null, "resource != null"); using (XmlReader propertiesFromEntry = GetPropertiesReaderFromEntry(item)) using (XmlReader propertiesFromContent = GetPropertiesReaderFromContent(item)) { XmlReader reader = null; if (resourceType.IsMediaLinkEntry) { if (propertiesFromContent != null) { // The server is expecting a MLE payload, however we found a <m:properties> element under the <atom:content> element. // The client must assumed the resource type is a non-MLE type and serialized as such. // // We must throw here otherwise the client would falsely assume all the properties are correctly deserialized on // the server where as in fact the properties element is under the wrong node. throw DataServiceException.CreateBadRequestError(Strings.DataServiceException_GeneralError); } reader = propertiesFromEntry; } else { if (propertiesFromEntry != null) { // The server is expecting a non-MLE payload, however we found a <m:properties> element under the <entry> element. // The client must assumed the resource type is a MLE type and serialized as such. // // We must throw here otherwise the client would falsely assume all the properties are correctly deserialized on // the server where as in fact the properties element is under the wrong node. throw DataServiceException.CreateBadRequestError(Strings.DataServiceException_GeneralError); } reader = propertiesFromContent; } if (reader != null) { reader.ReadStartElement(XmlConstants.AtomPropertiesElementName, XmlConstants.DataWebMetadataNamespace); PlainXmlDeserializer.ApplyContent(this, reader, resourceType, resource, propertiesApplied, this.MaxObjectCount); } } }