private void ApplyNavigationProperty(ODataNavigationLink navigationLink, IEdmNavigationProperty navigationProperty, object entityResource, ODataDeserializerContext readContext) { ODataNavigationLinkAnnotation navigationLinkAnnotation = navigationLink.GetAnnotation <ODataNavigationLinkAnnotation>(); Contract.Assert(navigationLinkAnnotation != null, "navigationLinkAnnotation != null"); Contract.Assert(navigationLink.IsCollection.HasValue, "We should know the cardinality of the navigation link by now."); foreach (ODataItem childItem in navigationLinkAnnotation) { ODataEntityReferenceLink entityReferenceLink = childItem as ODataEntityReferenceLink; if (entityReferenceLink != null) { // ignore links. continue; } ODataFeed feed = childItem as ODataFeed; if (feed != null) { ApplyFeedInNavigationProperty(navigationProperty, entityResource, feed, readContext); continue; } // It must be entry by now. ODataEntry entry = (ODataEntry)childItem; if (entry != null) { ApplyEntryInNavigationProperty(navigationProperty, entityResource, entry, readContext); } } }
/// <summary> /// Applies a navigation link as a navigation property. /// </summary> /// <param name="navigationLink">The navigation link read from the payload to apply.</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="navigationProperty">The navigation property which coresponds with the navigation link.</param> /// <param name="entityResource">The entity resource to apply the properties to.</param> private void ApplyNavigationProperty( ODataNavigationLink navigationLink, ResourceSetWrapper entityResourceSet, ResourceType entityResourceType, ResourceProperty navigationProperty, object entityResource) { Debug.Assert(navigationLink != null, "navigationLink != 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(navigationProperty != null && navigationProperty.TypeKind == ResourceTypeKind.EntityType, "navigationProperty != null && navigationProperty.TypeKind == ResourceTypeKind.EntityType"); Debug.Assert(navigationLink.Name == navigationProperty.Name, "The navigationProperty must have the same name as the navigationLink to apply."); Debug.Assert(entityResource != null, "entityResource != null"); ResourceSetWrapper targetResourceSet = null; // Get the target resource set for the navigation property, this also causes validation of the resource set and thus migth fail. // WCF DS Used to do this in ATOM only if the link was expanded, which was a security issue we decided to fix, so now we do it always. targetResourceSet = this.GetNavigationPropertyTargetResourceSet(entityResourceSet, entityResourceType, navigationProperty); ODataNavigationLinkAnnotation navigationLinkAnnotation = navigationLink.GetAnnotation <ODataNavigationLinkAnnotation>(); Debug.Assert(navigationLinkAnnotation != null, "navigationLinkAnnotation != null"); Debug.Assert(navigationLinkAnnotation.Count > 0, "Each navigation link must have at least one child in request."); Debug.Assert(navigationLink.IsCollection.HasValue, "We should know the cardinality of the navigation link by now."); // We have to allow IsCollection=false for ResourceSetReference properties as well. Debug.Assert( navigationLink.IsCollection == true || (navigationLinkAnnotation.Count == 1), "If the navigation link is a singleton, then there must be exactly one child in it and the property must be a singleton as well."); Debug.Assert( navigationLink.IsCollection == false || (navigationProperty.Kind == ResourcePropertyKind.ResourceSetReference), "If the navigation link is a collection, then the property must be a collection as well."); // Just loop through the navigation link content // Note that if the navigation link is a singleton it must have just one item in it, so it's OK to loop anyway. foreach (ODataItem childItem in navigationLinkAnnotation) { ODataEntityReferenceLink entityReferenceLink = childItem as ODataEntityReferenceLink; if (entityReferenceLink != null) { this.ApplyEntityReferenceLinkInNavigationProperty(navigationProperty, entityResource, entityReferenceLink); continue; } ODataFeed feed = childItem as ODataFeed; if (feed != null) { this.ApplyFeedInNavigationProperty(navigationProperty, targetResourceSet, entityResource, feed); continue; } // It must be entry by now. ODataEntry entry = (ODataEntry)childItem; this.ApplyEntryInNavigationProperty(navigationProperty, targetResourceSet, entityResource, entry); } }
internal static ODataItem ReadEntryOrFeed(ODataReader odataReader, ODataDeserializerContext readContext) { ODataItem topLevelItem = null; Stack <ODataItem> itemsStack = new Stack <ODataItem>(); while (odataReader.Read()) { 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) { Contract.Assert(entry != null, "The top-level entry can never be null."); topLevelItem = entry; } else { ODataItem parentItem = itemsStack.Peek(); ODataFeed parentFeed = parentItem as ODataFeed; if (parentFeed != null) { ODataFeedAnnotation parentFeedAnnotation = parentFeed.GetAnnotation <ODataFeedAnnotation>(); Contract.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>(); Contract.Assert(parentNavigationLinkAnnotation != null, "Every navigation link we added to the stack should have the navigation link annotation on it."); Contract.Assert(parentNavigationLink.IsCollection == false, "Only singleton navigation properties can contain entry as their child."); Contract.Assert(parentNavigationLinkAnnotation.Count == 0, "Each navigation property can contain only one entry as its direct child."); parentNavigationLinkAnnotation.Add(entry); } } itemsStack.Push(entry); RecurseEnter(readContext); break; case ODataReaderState.EntryEnd: Contract.Assert(itemsStack.Count > 0 && itemsStack.Peek() == odataReader.Item, "The entry which is ending should be on the top of the items stack."); itemsStack.Pop(); RecurseLeave(readContext); break; case ODataReaderState.NavigationLinkStart: ODataNavigationLink navigationLink = (ODataNavigationLink)odataReader.Item; Contract.Assert(navigationLink != null, "Navigation link should never be null."); navigationLink.SetAnnotation(new ODataNavigationLinkAnnotation()); Contract.Assert(itemsStack.Count > 0, "Navigation link can't appear as top-level item."); { ODataEntry parentEntry = (ODataEntry)itemsStack.Peek(); ODataEntryAnnotation parentEntryAnnotation = parentEntry.GetAnnotation <ODataEntryAnnotation>(); Contract.Assert(parentEntryAnnotation != null, "Every entry we added to the stack should have the entry annotation on it."); parentEntryAnnotation.Add(navigationLink); } itemsStack.Push(navigationLink); RecurseEnter(readContext); break; case ODataReaderState.NavigationLinkEnd: Contract.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(); RecurseLeave(readContext); break; case ODataReaderState.FeedStart: ODataFeed feed = (ODataFeed)odataReader.Item; Contract.Assert(feed != null, "Feed should never be null."); feed.SetAnnotation(new ODataFeedAnnotation()); if (itemsStack.Count > 0) { ODataNavigationLink parentNavigationLink = (ODataNavigationLink)itemsStack.Peek(); Contract.Assert(parentNavigationLink != null, "this has to be an inner feed. inner feeds always have a navigation link."); ODataNavigationLinkAnnotation parentNavigationLinkAnnotation = parentNavigationLink.GetAnnotation <ODataNavigationLinkAnnotation>(); Contract.Assert(parentNavigationLinkAnnotation != null, "Every navigation link we added to the stack should have the navigation link annotation on it."); Contract.Assert(parentNavigationLink.IsCollection == true, "Only collection navigation properties can contain feed as their child."); parentNavigationLinkAnnotation.Add(feed); } else { topLevelItem = feed; } itemsStack.Push(feed); RecurseEnter(readContext); break; case ODataReaderState.FeedEnd: Contract.Assert(itemsStack.Count > 0 && itemsStack.Peek() == odataReader.Item, "The feed which is ending should be on the top of the items stack."); itemsStack.Pop(); RecurseLeave(readContext); break; case ODataReaderState.EntityReferenceLink: ODataEntityReferenceLink entityReferenceLink = (ODataEntityReferenceLink)odataReader.Item; Contract.Assert(entityReferenceLink != null, "Entity reference link should never be null."); Contract.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>(); Contract.Assert(parentNavigationLinkAnnotation != null, "Every navigation link we added to the stack should have the navigation link annotation on it."); parentNavigationLinkAnnotation.Add(entityReferenceLink); } break; default: Contract.Assert(false, "We should never get here, it means the ODataReader reported a wrong state."); break; } } Contract.Assert(odataReader.State == ODataReaderState.Completed, "We should have consumed all of the input by now."); Contract.Assert(topLevelItem != null, "A top level entry or feed should have been read by now."); return(topLevelItem); }
/// <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); }