protected override object Read(System.Data.Services.SegmentInfo segmentInfo) { ODataEntry entry; ResourceType targetResourceType = segmentInfo.TargetResourceType; IEdmEntityType schemaType = (IEdmEntityType)base.GetSchemaType(targetResourceType); ODataReader odataReader = base.MessageReader.CreateODataEntryReader(schemaType); try { entry = this.ReadEntry(odataReader, segmentInfo); } catch (UriFormatException exception) { if (this.ContentFormat == ContentFormat.Atom) { throw DataServiceException.CreateBadRequestError(System.Data.Services.Strings.Syndication_ErrorReadingEntry(exception.Message), exception); } throw; } ODataEntryAnnotation entryAnnotation = entry.GetAnnotation <ODataEntryAnnotation>(); base.RecurseEnter(); this.ApplyEntityProperties(segmentInfo, entry, entryAnnotation); base.RecurseLeave(); return(entryAnnotation.EntityResource); }
/// <summary> /// Creates or gets an entity resource token instance based on the data from entry in the payload. /// The resource is then set on the entry annotation. /// </summary> /// <param name="segmentInfo">The segment info describing the entity in question.</param> /// <param name="entry">The OData entry instance read from the payload.</param> /// <param name="entryAnnotation">The entry annotation for the entry to process.</param> /// <param name="topLevel">true if this is a top-level entry, false otherwise.</param> private void CreateEntityResource(SegmentInfo segmentInfo, ODataEntry entry, ODataEntryAnnotation entryAnnotation, bool topLevel) { Debug.Assert(segmentInfo != null, "segmentInfo != null"); Debug.Assert(entry != null, "Null entries should not be tried to translated to entity instances, instead they should be handled separately."); Debug.Assert(entryAnnotation != null, "entryAnnotation != null"); // Note that this method does not call RecurseEnter and RecurseLeave. // It is going to be called when reading the top-level entry during entry reading (in which case it would count 1, and not fail anyway) // and then during entity materialization, but then we're going to call the ApplyEntityProperties on all the entities // and so we will count the recursion limits there instead. // Update the object count everytime you encounter a new resource. this.CheckAndIncrementObjectCount(); // Get the type from the entry. ResourceType entityResourceType = this.GetEntryResourceType(entry, segmentInfo.TargetResourceType); Debug.Assert(entityResourceType != null && entityResourceType.ResourceTypeKind == ResourceTypeKind.EntityType, "Each entity must have an entity type."); // We have the actual type info from the payload. Update the request/response DSV based on the particular type. this.UpdateAndCheckRequestResponseDSV(entityResourceType, topLevel); if (segmentInfo.TargetKind == RequestTargetKind.OpenProperty) { // Open navigation properties are not supported on OpenTypes. throw DataServiceException.CreateBadRequestError(Microsoft.OData.Service.Strings.OpenNavigationPropertiesNotSupportedOnOpenTypes(segmentInfo.Identifier)); } Debug.Assert(segmentInfo.TargetKind == RequestTargetKind.Resource, "Only resource targets can accept entity payloads."); object entityResource; if (this.Update) { Debug.Assert(topLevel, "Updates don't allow deep payload, only deep links."); // If it's a top level resource, then it cannot be null. // [Astoria-ODataLib-Integration] WCF DS Server doesn't check ETags if an ATOM payload entry has no content and no links (and it's a V1 entry) // We decided to break WCF DS and always check ETag - seems like a security issue. entityResource = this.GetObjectFromSegmentInfo( entityResourceType, segmentInfo, true /*verifyETag*/, true /*checkForNull*/, this.Service.OperationContext.RequestMessage.HttpVerb == HttpVerbs.PUT /*replaceResource*/); } else { // Check for append rights whenever we need to create a resource. DataServiceConfiguration.CheckResourceRights(segmentInfo.TargetResourceSet, EntitySetRights.WriteAppend); // Create new instance of the entity. entityResource = this.Updatable.CreateResource(segmentInfo.TargetResourceSet.Name, entityResourceType.FullName); } entryAnnotation.EntityResource = entityResource; entryAnnotation.EntityResourceType = entityResourceType; }
private void ApplyNavigationProperties(ODataEntryAnnotation entryAnnotation, ResourceSetWrapper entityResourceSet, ResourceType entityResourceType, object entityResource) { foreach (ODataNavigationLink link in entryAnnotation) { ResourcePropertyKind stream = ResourcePropertyKind.Stream; ResourceProperty navigationProperty = entityResourceType.TryResolvePropertyName(link.Name, stream); this.ApplyNavigationProperty(link, entityResourceSet, entityResourceType, navigationProperty, entityResource); } }
private object CreateNestedEntityAndApplyProperties(System.Data.Services.SegmentInfo segmentInfo, ODataEntry entry) { ODataEntryAnnotation entryAnnotation = entry.GetAnnotation <ODataEntryAnnotation>(); base.RecurseEnter(); this.CreateEntityResource(segmentInfo, entry, entryAnnotation, false); this.ApplyEntityProperties(segmentInfo, entry, entryAnnotation); base.RecurseLeave(); return(entryAnnotation.EntityResource); }
/// <summary> /// Reads the input request payload and returns the WCF DS value representation of it. /// </summary> /// <param name="segmentInfo">Info about the request to read.</param> /// <returns>The WCF DS representation of the value read.</returns> protected override object Read(SegmentInfo segmentInfo) { Debug.Assert(segmentInfo != null, "segmentInfo != null"); Debug.Assert(segmentInfo.TargetKind == RequestTargetKind.Resource, "The EntityDeserializer only supports Resource target kinds."); ResourceType expectedResourceType = segmentInfo.TargetResourceType; Debug.Assert(expectedResourceType != null, "To read an entity we must know the expected resource type of the entity to read."); Debug.Assert(expectedResourceType.ResourceTypeKind == ResourceTypeKind.EntityType, "Only entity types can be used as types for entities."); IEdmEntityType expectedEntityType = (IEdmEntityType)this.GetSchemaType(expectedResourceType); MetadataProviderEdmModel metadataProviderEdmModel = this.Service.Provider.GetMetadataProviderEdmModel(); Debug.Assert(metadataProviderEdmModel.Mode == MetadataProviderEdmModelMode.Serialization, "Model expected to be in serialization mode."); IEdmEntitySet expectedEntitySet = WebUtil.GetEntitySet(this.Service.Provider, metadataProviderEdmModel, segmentInfo.TargetResourceSet); ODataReader odataReader = this.MessageReader.CreateODataEntryReader(expectedEntitySet, expectedEntityType); #pragma warning disable 618 AssertReaderFormatIsExpected(this.MessageReader, ODataFormat.Atom, ODataFormat.Json); #pragma warning restore 618 // Read the entry and all its children into a tree. We use annotation to connect the items into the tree. // Note that we must cache the entire payload to preserve call order for navigation properties. // Due to the fact that the payload order on the wire is arbitrary, but we always set all non-navigation properties first // and then apply all navigation properties, we must cache the entire tree. ODataEntry topLevelEntry; try { topLevelEntry = this.ReadEntry(odataReader, segmentInfo); } catch (UriFormatException exception) { // For backward compatibility with previous released when reading ATOM we need to catch UriFormatExceptions and wrap them // in a bad request exception so that a 400 is reported back. In JSON we used to not do this and thus we need to continue // throwing the original exception which will cause a 500. if (this.IsAtomRequest) { throw DataServiceException.CreateBadRequestError(Microsoft.OData.Service.Strings.Syndication_ErrorReadingEntry(exception.Message), exception); } throw; } ODataEntryAnnotation topLevelEntryAnnotation = topLevelEntry.GetAnnotation <ODataEntryAnnotation>(); Debug.Assert(topLevelEntryAnnotation != null, "Each entry we read must have the entry annotation."); this.RecurseEnter(); this.ApplyEntityProperties(segmentInfo, topLevelEntry, topLevelEntryAnnotation); this.RecurseLeave(); return(topLevelEntryAnnotation); }
/// <summary> /// Create the entity resource update token and applies properties and navigation links to the entity resource token instance /// based on the data from entry in the payload. /// </summary> /// <param name="segmentInfo">The segment info describing the entity in question.</param> /// <param name="entry">The OData entry instance read from the payload.</param> /// <returns>The entity resource update token for the created entity.</returns> /// <remarks>This method should only be called on nested entries!</remarks> private object CreateNestedEntityAndApplyProperties(SegmentInfo segmentInfo, ODataEntry entry) { Debug.Assert(segmentInfo != null, "segmentInfo != null"); Debug.Assert(entry != null, "entry != null"); ODataEntryAnnotation entryAnnotation = entry.GetAnnotation <ODataEntryAnnotation>(); Debug.Assert(entryAnnotation != null, "Each entry we read must have our entry annotation."); this.RecurseEnter(); this.CreateEntityResource(segmentInfo, entry, entryAnnotation, /*topLevel*/ false); this.ApplyEntityProperties(segmentInfo, entry, entryAnnotation); this.RecurseLeave(); return(entryAnnotation.EntityResource); }
/// <summary> /// Applies navigation properties. /// </summary> /// <param name="entryAnnotation">The entry annotation for the entry to process.</param> /// <param name="entityResourceSet">The resource set into which the entity belongs to.</param> /// <param name="entityResourceType">The type of the entity to apply the properties to.</param> /// <param name="entityResource">The entity resource to apply the properties to.</param> private void ApplyNavigationProperties( ODataEntryAnnotation entryAnnotation, ResourceSetWrapper entityResourceSet, ResourceType entityResourceType, object entityResource) { Debug.Assert(entryAnnotation != null, "entryAnnotation != null"); Debug.Assert(entityResourceSet != null, "entityResourceSet != null"); Debug.Assert(entityResourceType != null, "entityResourceType != null"); Debug.Assert(entityResourceType.ResourceTypeKind == ResourceTypeKind.EntityType, "Only entity types can be specified for entities."); Debug.Assert(entityResource != null, "entityResource != null"); foreach (ODataNavigationLink navigationLink in entryAnnotation) { ResourceProperty navigationProperty = entityResourceType.TryResolvePropertyName(navigationLink.Name, exceptKind: ResourcePropertyKind.Stream); Debug.Assert(navigationProperty != null, "ODataLib reader should have already validated that all navigation properties are declared and none is open."); Debug.Assert(navigationProperty.TypeKind == ResourceTypeKind.EntityType, "Navigation properties should already be validated by ODataLib to be of entity types."); this.ApplyNavigationProperty(navigationLink, entityResourceSet, entityResourceType, navigationProperty, entityResource); } }
/// <summary> /// Reads an entry from the <paramref name="odataReader"/> and all it's children including expanded entries and feeds. /// </summary> /// <param name="odataReader">The ODataReader to read from.</param> /// <param name="topLevelSegmentInfo">The segment info for the top-level entry to read.</param> /// <returns>The <see cref="ODataEntry"/> with annotations which store the navigation links and their expanded values.</returns> private ODataEntry ReadEntry(ODataReader odataReader, SegmentInfo topLevelSegmentInfo) { Debug.Assert(odataReader != null, "odataReader != null"); Debug.Assert(odataReader.State == ODataReaderState.Start, "The ODataReader must not have been used yet."); Debug.Assert(topLevelSegmentInfo != null, "topLevelSegmentInfo != null"); ODataEntry topLevelEntry = null; Stack <ODataItem> itemsStack = new Stack <ODataItem>(); while (odataReader.Read()) { // Don't let the item stack grow larger than we can process later. Also lets us to fail and report the recursion depth limit error before ODL does. if (itemsStack.Count >= RecursionLimit) { throw DataServiceException.CreateDeepRecursion(RecursionLimit); } switch (odataReader.State) { case ODataReaderState.EntryStart: ODataEntry entry = (ODataEntry)odataReader.Item; ODataEntryAnnotation entryAnnotation = null; if (entry != null) { entryAnnotation = new ODataEntryAnnotation(); entry.SetAnnotation(entryAnnotation); } if (itemsStack.Count == 0) { Debug.Assert(entry != null, "The top-level entry can never be null."); topLevelEntry = entry; // For top-level entry, create the entry resource here. // This is needed since the creation of the resource may fail (especially if this is an update in which case // we evaluate the URL query, which may return no results and thus we would fail with 404) // and we need to report that failure rather then potential other failures caused by the properties in the entry in question. this.CreateEntityResource(topLevelSegmentInfo, entry, entryAnnotation, /*topLevel*/ true); } else { ODataItem parentItem = itemsStack.Peek(); ODataFeed parentFeed = parentItem as ODataFeed; if (parentFeed != null) { ODataFeedAnnotation parentFeedAnnotation = parentFeed.GetAnnotation <ODataFeedAnnotation>(); Debug.Assert(parentFeedAnnotation != null, "Every feed we added to the stack should have the feed annotation on it."); parentFeedAnnotation.Add(entry); } else { ODataNavigationLink parentNavigationLink = (ODataNavigationLink)parentItem; ODataNavigationLinkAnnotation parentNavigationLinkAnnotation = parentNavigationLink.GetAnnotation <ODataNavigationLinkAnnotation>(); Debug.Assert(parentNavigationLinkAnnotation != null, "Every navigation link we added to the stack should have the navigation link annotation on it."); Debug.Assert(parentNavigationLink.IsCollection == false, "Only singleton navigation properties can contain entry as their child."); Debug.Assert(parentNavigationLinkAnnotation.Count == 0, "Each navigation property can contain only one entry as its direct child."); parentNavigationLinkAnnotation.Add(entry); } } itemsStack.Push(entry); break; case ODataReaderState.EntryEnd: Debug.Assert(itemsStack.Count > 0 && itemsStack.Peek() == odataReader.Item, "The entry which is ending should be on the top of the items stack."); itemsStack.Pop(); break; case ODataReaderState.NavigationLinkStart: ODataNavigationLink navigationLink = (ODataNavigationLink)odataReader.Item; Debug.Assert(navigationLink != null, "Navigation link should never be null."); navigationLink.SetAnnotation(new ODataNavigationLinkAnnotation()); Debug.Assert(itemsStack.Count > 0, "Navigation link can't appear as top-level item."); { ODataEntry parentEntry = (ODataEntry)itemsStack.Peek(); ODataEntryAnnotation parentEntryAnnotation = parentEntry.GetAnnotation <ODataEntryAnnotation>(); Debug.Assert(parentEntryAnnotation != null, "Every entry we added to the stack should have the navigation link annotation on it."); parentEntryAnnotation.Add(navigationLink); } itemsStack.Push(navigationLink); break; case ODataReaderState.NavigationLinkEnd: Debug.Assert(itemsStack.Count > 0 && itemsStack.Peek() == odataReader.Item, "The navigation link which is ending should be on the top of the items stack."); itemsStack.Pop(); break; case ODataReaderState.FeedStart: ODataFeed feed = (ODataFeed)odataReader.Item; Debug.Assert(feed != null, "Feed should never be null."); feed.SetAnnotation(new ODataFeedAnnotation()); Debug.Assert(itemsStack.Count > 0, "Since we always start reading entry, we should never get a feed as the top-level item."); { ODataNavigationLink parentNavigationLink = (ODataNavigationLink)itemsStack.Peek(); ODataNavigationLinkAnnotation parentNavigationLinkAnnotation = parentNavigationLink.GetAnnotation <ODataNavigationLinkAnnotation>(); Debug.Assert(parentNavigationLinkAnnotation != null, "Every navigation link we added to the stack should have the navigation link annotation on it."); Debug.Assert(parentNavigationLink.IsCollection == true, "Only collection navigation properties can contain feed as their child."); parentNavigationLinkAnnotation.Add(feed); } itemsStack.Push(feed); break; case ODataReaderState.FeedEnd: Debug.Assert(itemsStack.Count > 0 && itemsStack.Peek() == odataReader.Item, "The feed which is ending should be on the top of the items stack."); itemsStack.Pop(); break; case ODataReaderState.EntityReferenceLink: ODataEntityReferenceLink entityReferenceLink = (ODataEntityReferenceLink)odataReader.Item; Debug.Assert(entityReferenceLink != null, "Entity reference link should never be null."); Debug.Assert(itemsStack.Count > 0, "Entity reference link should never be reported as top-level item."); { ODataNavigationLink parentNavigationLink = (ODataNavigationLink)itemsStack.Peek(); ODataNavigationLinkAnnotation parentNavigationLinkAnnotation = parentNavigationLink.GetAnnotation <ODataNavigationLinkAnnotation>(); Debug.Assert(parentNavigationLinkAnnotation != null, "Every navigation link we added to the stack should have the navigation link annotation on it."); parentNavigationLinkAnnotation.Add(entityReferenceLink); } break; default: Debug.Assert(false, "We should never get here, it means the ODataReader reported a wrong state."); break; } } Debug.Assert(odataReader.State == ODataReaderState.Completed, "We should have consumed all of the input by now."); Debug.Assert(topLevelEntry != null, "A top level entry should have been read by now."); return(topLevelEntry); }
/// <summary> /// Applies properties and navigation links to the entity resource token instance based on the data from entry in the payload. /// </summary> /// <param name="segmentInfo">The segment info describing the entity in question.</param> /// <param name="entry">The OData entry instance read from the payload.</param> /// <param name="entryAnnotation">The entry annotation for the entry to process.</param> private void ApplyEntityProperties(SegmentInfo segmentInfo, ODataEntry entry, ODataEntryAnnotation entryAnnotation) { Debug.Assert(segmentInfo != null, "segmentInfo != null"); Debug.Assert(entry != null, "Null entries should not be tried to translated to entity instances, instead they should be handled separately."); Debug.Assert(entryAnnotation != null, "entryAnnotation != null"); // Note that recursion count is handled by the caller of this method. // We need this since we need to call it around the entire CreateEntryResource + ApplyEntityProperties combo for the nested entries // and will call it separately for the top-level entry at the very root. object entityResource = entryAnnotation.EntityResource; ResourceType entityResourceType = entryAnnotation.EntityResourceType; // First apply all non-navigation properties. this.ApplyValueProperties(entry, entityResourceType, entityResource); // And then apply all navigation properties. this.ApplyNavigationProperties(entryAnnotation, segmentInfo.TargetResourceSet, entityResourceType, entityResource); Debug.Assert(this.Tracker != null, "this.Tracker != null"); // Track the operation if (this.Update) { // Originally JSON used to fire the change interceptor only if something actually changed // We decided to make it consistent and fire the change interceptor always. this.Tracker.TrackAction(entityResource, segmentInfo.TargetResourceSet, UpdateOperations.Change); } else { this.Tracker.TrackAction(entityResource, segmentInfo.TargetResourceSet, UpdateOperations.Add); } }
private ODataEntry ReadEntry(ODataReader odataReader, System.Data.Services.SegmentInfo topLevelSegmentInfo) { ODataEntry entry = null; Stack <ODataItem> stack = new Stack <ODataItem>(); while (odataReader.Read()) { if (stack.Count >= 100) { throw DataServiceException.CreateDeepRecursion(100); } switch (odataReader.State) { case ODataReaderState.FeedStart: { ODataFeed feed2 = (ODataFeed)odataReader.Item; feed2.SetAnnotation <ODataFeedAnnotation>(new ODataFeedAnnotation()); ODataNavigationLink link3 = (ODataNavigationLink)stack.Peek(); link3.GetAnnotation <ODataNavigationLinkAnnotation>().Add(feed2); stack.Push(feed2); break; } case ODataReaderState.FeedEnd: stack.Pop(); break; case ODataReaderState.EntryStart: { ODataEntry entry2 = (ODataEntry)odataReader.Item; ODataEntryAnnotation annotation = null; if (entry2 != null) { annotation = new ODataEntryAnnotation(); entry2.SetAnnotation <ODataEntryAnnotation>(annotation); } if (stack.Count == 0) { entry = entry2; this.CreateEntityResource(topLevelSegmentInfo, entry2, annotation, true); } else { ODataItem item = stack.Peek(); ODataFeed feed = item as ODataFeed; if (feed != null) { feed.GetAnnotation <ODataFeedAnnotation>().Add(entry2); } else { ODataNavigationLink link = (ODataNavigationLink)item; link.GetAnnotation <ODataNavigationLinkAnnotation>().Add(entry2); } } stack.Push(entry2); break; } case ODataReaderState.EntryEnd: stack.Pop(); break; case ODataReaderState.NavigationLinkStart: { ODataNavigationLink link2 = (ODataNavigationLink)odataReader.Item; link2.SetAnnotation <ODataNavigationLinkAnnotation>(new ODataNavigationLinkAnnotation()); ODataEntry entry3 = (ODataEntry)stack.Peek(); entry3.GetAnnotation <ODataEntryAnnotation>().Add(link2); stack.Push(link2); break; } case ODataReaderState.NavigationLinkEnd: stack.Pop(); break; case ODataReaderState.EntityReferenceLink: { ODataEntityReferenceLink link4 = (ODataEntityReferenceLink)odataReader.Item; ODataNavigationLink link5 = (ODataNavigationLink)stack.Peek(); link5.GetAnnotation <ODataNavigationLinkAnnotation>().Add(link4); break; } } } return(entry); }
private void ApplyEntityProperties(System.Data.Services.SegmentInfo segmentInfo, ODataEntry entry, ODataEntryAnnotation entryAnnotation) { object entityResource = entryAnnotation.EntityResource; ResourceType entityResourceType = entryAnnotation.EntityResourceType; this.ApplyValueProperties(entry, entityResourceType, entityResource); this.ApplyNavigationProperties(entryAnnotation, segmentInfo.TargetContainer, entityResourceType, entityResource); if (base.Update) { base.Tracker.TrackAction(entityResource, segmentInfo.TargetContainer, UpdateOperations.Change); } else { base.Tracker.TrackAction(entityResource, segmentInfo.TargetContainer, UpdateOperations.Add); } }
private void CreateEntityResource(System.Data.Services.SegmentInfo segmentInfo, ODataEntry entry, ODataEntryAnnotation entryAnnotation, bool topLevel) { object obj2; base.CheckAndIncrementObjectCount(); ResourceType entryResourceType = this.GetEntryResourceType(entry, segmentInfo.TargetResourceType); base.UpdateAndCheckRequestResponseDSV(entryResourceType, topLevel); if (segmentInfo.TargetKind == RequestTargetKind.OpenProperty) { throw DataServiceException.CreateBadRequestError(System.Data.Services.Strings.OpenNavigationPropertiesNotSupportedOnOpenTypes(segmentInfo.Identifier)); } if (base.Update) { obj2 = base.GetObjectFromSegmentInfo(entryResourceType, segmentInfo, true, true, base.Service.OperationContext.Host.HttpVerb == HttpVerbs.PUT); } else { DataServiceConfiguration.CheckResourceRights(segmentInfo.TargetContainer, EntitySetRights.WriteAppend); obj2 = base.Updatable.CreateResource(segmentInfo.TargetContainer.Name, entryResourceType.FullName); } entryAnnotation.EntityResource = obj2; entryAnnotation.EntityResourceType = entryResourceType; }