public ODataNavigationLinkTests()
        {
            this.navigationLink = new ODataNavigationLink();

            var entry = new ODataEntry
            {
                TypeName = "ns.DerivedType",
                Properties = new[]
                {
                    new ODataProperty{Name = "Id", Value = 1, SerializationInfo = new ODataPropertySerializationInfo{PropertyKind = ODataPropertyKind.Key}},
                    new ODataProperty{Name = "Name", Value = "Bob", SerializationInfo = new ODataPropertySerializationInfo{PropertyKind = ODataPropertyKind.ETag}}
                }
            };

            var serializationInfo = new ODataFeedAndEntrySerializationInfo { NavigationSourceName = "Set", NavigationSourceEntityTypeName = "ns.BaseType", ExpectedTypeName = "ns.BaseType" };
            var typeContext = ODataFeedAndEntryTypeContext.Create(serializationInfo, null, null, null, EdmCoreModel.Instance, true);
            var metadataContext = new TestMetadataContext();
            var entryMetadataContext = ODataEntryMetadataContext.Create(entry, typeContext, serializationInfo, null, metadataContext, SelectedPropertiesNode.EntireSubtree);
            var metadataBuilder = new ODataConventionalEntityMetadataBuilder(entryMetadataContext, metadataContext, new ODataConventionalUriBuilder(ServiceUri, UrlConvention.CreateWithExplicitValue(false)));
            this.navigationLinkWithFullBuilder = new ODataNavigationLink { Name = "NavProp" };
            this.navigationLinkWithFullBuilder.MetadataBuilder = metadataBuilder;

            this.navigationLinkWithNoOpBuilder = new ODataNavigationLink { Name = "NavProp" };
            this.navigationLinkWithNoOpBuilder.MetadataBuilder = new NoOpEntityMetadataBuilder(entry);

            this.navigationLinkWithNullBuilder = new ODataNavigationLink { Name = "NavProp" };
            this.navigationLinkWithNullBuilder.MetadataBuilder = ODataEntityMetadataBuilder.Null;
        }
Beispiel #2
0
        /// <summary>
        /// Creates the value for the navigation property's link relation attribute.
        /// </summary>
        /// <param name="navigationLink">The link representing the navigation property for which the relation value is created.</param>
        /// <returns>The relation attribute value for the navigation property's link relation.</returns>
        internal static string ComputeODataNavigationLinkRelation(ODataNavigationLink navigationLink)
        {
            Debug.Assert(navigationLink != null, "navigationLink != null");
            Debug.Assert(navigationLink.Name != null, "navigationLink.Name != null");

            return string.Join("", new string[] { AtomConstants.ODataNavigationPropertiesRelatedLinkRelationPrefix, navigationLink.Name });
        }
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="navigationLink">The navigation link.</param>
        /// <param name="navigationProperty">The navigation property for the link, if it's available.</param>
        internal ODataAtomReaderNavigationLinkDescriptor(ODataNavigationLink navigationLink, IEdmNavigationProperty navigationProperty)
        {
            Debug.Assert(navigationLink != null, "navigationLink != null");

            this.navigationLink = navigationLink;
            this.navigationProperty = navigationProperty;
        }
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="navigationLink">The navigation link.</param>
        /// <param name="navigationProperty">The navigation property for the link, if it's available.</param>
        internal ODataAtomReaderNavigationLinkDescriptor(ODataNavigationLink navigationLink, IEdmNavigationProperty navigationProperty)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(navigationLink != null, "navigationLink != null");

            this.navigationLink = navigationLink;
            this.navigationProperty = navigationProperty;
        }
Beispiel #5
0
        /// <summary>
        /// Creates the value for the navigation property's type attribute.
        /// </summary>
        /// <param name="navigationLink">The link representing the navigation property for which the type value is created.</param>
        /// <returns>The type attribute value for the navigation property.</returns>
        internal static string ComputeODataNavigationLinkType(ODataNavigationLink navigationLink)
        {
            Debug.Assert(navigationLink != null, "navigationLink != null");
            Debug.Assert(navigationLink.IsCollection.HasValue, "navigationLink.IsCollection.HasValue");

            // "application/atom+xml;type=entry" or type="application/atom+xml;type=feed"
            return navigationLink.IsCollection.Value ? MimeConstants.MimeApplicationAtomXmlTypeFeed : MimeConstants.MimeApplicationAtomXmlTypeEntry;
        }
Beispiel #6
0
        /// <summary>
        /// Creates the value for the navigation property's link relation attribute.
        /// </summary>
        /// <param name="navigationLink">The link representing the navigation property for which the relation value is created.</param>
        /// <returns>The relation attribute value for the navigation property's link relation.</returns>
        internal static string ComputeODataNavigationLinkRelation(ODataNavigationLink navigationLink)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(navigationLink != null, "navigationLink != null");
            Debug.Assert(navigationLink.Name != null, "navigationLink.Name != null");

            return string.Join("/", new string[] { AtomConstants.ODataNamespace, AtomConstants.ODataNavigationPropertiesRelatedSegmentName, navigationLink.Name });
        }
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="navigationLink">The navigation link to report.</param>
        /// <param name="navigationProperty">The navigation property for which the link will be reported.</param>
        /// <param name="isExpanded">true if the navigation link is expanded.</param>
        private ODataJsonLightReaderNavigationLinkInfo(ODataNavigationLink navigationLink, IEdmNavigationProperty navigationProperty, bool isExpanded)
        {
            Debug.Assert(navigationLink != null, "navigationLink != null");
            Debug.Assert(navigationProperty == null || navigationProperty.Name == navigationLink.Name, "The name of the navigation link doesn't match the name of the property.");

            this.navigationLink = navigationLink;
            this.navigationProperty = navigationProperty;
            this.isExpanded = isExpanded;
        }
Beispiel #8
0
        /// <summary>
        /// Finish writing a navigation link with content.
        /// </summary>
        /// <param name="navigationLink">The navigation link to write.</param>
        protected override void EndNavigationLinkWithContent(ODataNavigationLink navigationLink)
        {
            Debug.Assert(navigationLink != null, "navigationLink != null");

            // We do not write the end element for atom:link here, since we need to write it for each item in the content separately.
            // See the detailed description in the StartNavigationLinkWithContent for details.
        }
        /// <summary>
        /// Reads a navigation link in entry element.
        /// </summary>
        /// <param name="entryState">The reader entry state for the entry being read.</param>
        /// <param name="linkRelation">The value of the rel attribute of the link to read, unescaped parsed URI.</param>
        /// <param name="linkHRef">The value of the href attribute of the link to read.</param>
        /// <returns>A descriptor of a navigation link if a navigation link was found; null otherwise.</returns>
        /// <remarks>
        /// Pre-Condition:  XmlNodeType.Element atom:link - the start tag of the atom:link element to read.
        /// Post-Condition: XmlNodeType.Element atom:link - the start tag of the atom:link element - the reader doesn't move
        /// </remarks>
        private ODataAtomReaderNavigationLinkDescriptor TryReadNavigationLinkInEntry(
            IODataAtomReaderEntryState entryState,
            string linkRelation,
            string linkHRef)
        {
            Debug.Assert(linkRelation != null, "linkRelation != null");
            this.XmlReader.AssertNotBuffering();
            this.AssertXmlCondition(XmlNodeType.Element);
            Debug.Assert(
                this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace && this.XmlReader.LocalName == AtomConstants.AtomLinkElementName,
                "The XML reader must be on the atom:link element for this method to work.");

            // We will ignore navigation links with empty property names
            string navigationLinkName = AtomUtils.GetNameFromAtomLinkRelationAttribute(linkRelation, AtomConstants.ODataNavigationPropertiesRelatedLinkRelationPrefix);
            if (string.IsNullOrEmpty(navigationLinkName))
            {
                return null;
            }

            // Lookup the property in metadata
            // Note that we already verified that the navigation link name is not empty.
            IEdmNavigationProperty navigationProperty = ReaderValidationUtils.ValidateNavigationPropertyDefined(navigationLinkName, entryState.EntityType, this.MessageReaderSettings);

            // Navigation link
            ODataNavigationLink navigationLink = new ODataNavigationLink { Name = navigationLinkName };

            // Get the type of the link
            string navigationLinkType = this.XmlReader.GetAttribute(this.AtomTypeAttributeName, this.EmptyNamespace);

            // [Astoria-ODataLib-Integration] Handling of type attribute value on atom:link element.
            // The behavior of ODataLib is:
            // Parse the type as content type
            //   Compare media type names ignoring case (as per spec), compare parameter type names ignoring case
            //   If it's application/atom+xml without type parameter or invalid type parameter - use it as a navigation link without specifying collection/singleton (pending metadata validation)
            //   If it's application/atom+xml with type='feed' - use it as a navigation link assuming it's a collection (pending metadata validation)
            //   If it's application/atom+xml with type='entry' - use it as a navigation link assuming it's a singleton (pending metadata validation)
            //   In any other case - skip this link and treat it as if it's not a navigation link.
            // Note that parsing the type means we may fail if it's not a valid content type.
            // Missing and invalid type attributes are allowed. We will infer the cardinality either from the model or when expanding the link.
            if (!string.IsNullOrEmpty(navigationLinkType))
            {
                // Fast path for most common link types
                bool hasEntryType, hasFeedType;
                bool isExactMatch = AtomUtils.IsExactNavigationLinkTypeMatch(navigationLinkType, out hasEntryType, out hasFeedType);
                if (!isExactMatch)
                {
                    // If the fast path did not work, we have to fully parse the media type.
                    string mediaTypeName, mediaTypeCharset;
                    IList<KeyValuePair<string, string>> contentTypeParameters = HttpUtils.ReadMimeType(navigationLinkType, out mediaTypeName, out mediaTypeCharset);
                    if (!HttpUtils.CompareMediaTypeNames(mediaTypeName, MimeConstants.MimeApplicationAtomXml))
                    {
                        return null;
                    }

                    string typeParameterValue = null;
                    if (contentTypeParameters != null)
                    {
                        for (int contentTypeParameterIndex = 0; contentTypeParameterIndex < contentTypeParameters.Count; contentTypeParameterIndex++)
                        {
                            KeyValuePair<string, string> contentTypeParameter = contentTypeParameters[contentTypeParameterIndex];
                            if (HttpUtils.CompareMediaTypeParameterNames(MimeConstants.MimeTypeParameterName, contentTypeParameter.Key))
                            {
                                typeParameterValue = contentTypeParameter.Value;
                                break;
                            }
                        }
                    }

                    if (typeParameterValue != null)
                    {
                        if (string.Compare(typeParameterValue, MimeConstants.MimeTypeParameterValueEntry, StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            hasEntryType = true;
                        }
                        else if (string.Compare(typeParameterValue, MimeConstants.MimeTypeParameterValueFeed, StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            hasFeedType = true;
                        }
                    }
                }

                if (hasEntryType)
                {
                    navigationLink.IsCollection = false;
                }
                else if (hasFeedType)
                {
                    navigationLink.IsCollection = true;
                }
            }

            // We allow missing HREF on a link and simply report null.
            if (linkHRef != null)
            {
                navigationLink.Url = this.ProcessUriFromPayload(linkHRef, this.XmlReader.XmlBaseUri);
            }

            this.XmlReader.MoveToElement();

            // Read and store ATOM link metadata (captures extra info like lang, title) if ATOM metadata reading is turned on.
            AtomLinkMetadata atomLinkMetadata = this.EntryMetadataDeserializer.ReadAtomLinkElementInEntryContent(linkRelation, linkHRef);
            if (atomLinkMetadata != null)
            {
                navigationLink.SetAnnotation(atomLinkMetadata);
            }

            return new ODataAtomReaderNavigationLinkDescriptor(navigationLink, navigationProperty);
        }
 /// <summary>
 /// Constructor to create a new JSON Light navigation link scope.
 /// </summary>
 /// <param name="writerState">The writer state for the new scope.</param>
 /// <param name="navLink">The navigation link for the new scope.</param>
 /// <param name="navigationSource">The navigation source we are going to write entities for.</param>
 /// <param name="entityType">The entity type for the entries in the feed to be written (or null if the entity set base type should be used).</param>
 /// <param name="skipWriting">true if the content of the scope to create should not be written.</param>
 /// <param name="selectedProperties">The selected properties of this scope.</param>
 /// <param name="odataUri">The ODataUri info of this scope.</param>
 internal JsonLightNavigationLinkScope(WriterState writerState, ODataNavigationLink navLink, IEdmNavigationSource navigationSource, IEdmEntityType entityType, bool skipWriting, SelectedPropertiesNode selectedProperties, ODataUri odataUri)
     : base(writerState, navLink, navigationSource, entityType, skipWriting, selectedProperties, odataUri)
 {
 }
 /// <summary>
 /// Creates a new JSON Light navigation link scope.
 /// </summary>
 /// <param name="writerState">The writer state for the new scope.</param>
 /// <param name="navLink">The navigation link for the new scope.</param>
 /// <param name="navigationSource">The navigation source we are going to write entities for.</param>
 /// <param name="entityType">The entity type for the entries in the feed to be written (or null if the entity set base type should be used).</param>
 /// <param name="skipWriting">true if the content of the scope to create should not be written.</param>
 /// <param name="selectedProperties">The selected properties of this scope.</param>
 /// <param name="odataUri">The ODataUri info of this scope.</param>
 /// <returns>The newly created JSON Light  navigation link scope.</returns>
 protected override NavigationLinkScope CreateNavigationLinkScope(WriterState writerState, ODataNavigationLink navLink, IEdmNavigationSource navigationSource, IEdmEntityType entityType, bool skipWriting, SelectedPropertiesNode selectedProperties, ODataUri odataUri)
 {
     return new JsonLightNavigationLinkScope(writerState, navLink, navigationSource, entityType, skipWriting, selectedProperties, odataUri);
 }
        /// <summary>
        /// Write an entity reference link.
        /// </summary>
        /// <param name="parentNavigationLink">The parent navigation link which is being written around the entity reference link.</param>
        /// <param name="entityReferenceLink">The entity reference link to write.</param>
        protected override void WriteEntityReferenceInNavigationLinkContent(ODataNavigationLink parentNavigationLink, ODataEntityReferenceLink entityReferenceLink)
        {
            Debug.Assert(parentNavigationLink != null, "parentNavigationLink != null");
            Debug.Assert(entityReferenceLink != null, "entityReferenceLink != null");
            Debug.Assert(!this.jsonLightOutputContext.WritingResponse, "Entity reference links are only supported in request, we should have verified this already.");

            // In JSON Light, we can only write entity reference links at the beginning of a navigation link in requests;
            // once we wrote a feed, entity reference links are not allowed anymore (we require all the entity reference
            // link to come first because of the grouping in the JSON Light wire format).
            JsonLightNavigationLinkScope navigationLinkScope = (JsonLightNavigationLinkScope)this.CurrentScope;
            if (navigationLinkScope.FeedWritten)
            {
                throw new ODataException(OData.Core.Strings.ODataJsonLightWriter_EntityReferenceLinkAfterFeedInRequest);
            }

            if (!navigationLinkScope.EntityReferenceLinkWritten)
            {
                // Write the property annotation for the entity reference link(s)
                this.odataAnnotationWriter.WritePropertyAnnotationName(parentNavigationLink.Name, ODataAnnotationNames.ODataBind);
                Debug.Assert(parentNavigationLink.IsCollection.HasValue, "parentNavigationLink.IsCollection.HasValue");
                if (parentNavigationLink.IsCollection.Value)
                {
                    this.jsonWriter.StartArrayScope();
                }

                navigationLinkScope.EntityReferenceLinkWritten = true;
            }

            Debug.Assert(entityReferenceLink.Url != null, "The entity reference link Url should have been validated by now.");
            this.jsonWriter.WriteValue(this.jsonLightEntryAndFeedSerializer.UriToString(entityReferenceLink.Url));
        }
        /// <summary>
        /// Finish writing a navigation link with content.
        /// </summary>
        /// <param name="navigationLink">The navigation link to write.</param>
        protected override void EndNavigationLinkWithContent(ODataNavigationLink navigationLink)
        {
            Debug.Assert(navigationLink != null, "navigationLink != null");

            if (!this.jsonLightOutputContext.WritingResponse)
            {
                JsonLightNavigationLinkScope navigationLinkScope = (JsonLightNavigationLinkScope)this.CurrentScope;

                // If we wrote entity reference links for a collection navigation property but no 
                // feed afterwards, we have to now close the array of links.
                if (navigationLinkScope.EntityReferenceLinkWritten && !navigationLinkScope.FeedWritten && navigationLink.IsCollection.Value)
                {
                    this.jsonWriter.EndArrayScope();
                }

                // In requests, the navigation link may have multiple entries in multiple feeds in it; if we 
                // wrote at least one feed, close the resulting array here.
                if (navigationLinkScope.FeedWritten)
                {
                    Debug.Assert(navigationLink.IsCollection.Value, "navigationLink.IsCollection.Value");
                    this.jsonWriter.EndArrayScope();
                }
            }
        }
        /// <summary>
        /// Creates a navigation link info for a singleton entity reference link.
        /// </summary>
        /// <param name="navigationLink">The navigation link to report.</param>
        /// <param name="navigationProperty">The navigation property for which the link will be reported.</param>
        /// <param name="entityReferenceLink">The entity reference link for the navigation link to report.</param>
        /// <param name="isExpanded">true if the navigation link is expanded.</param>
        /// <returns>The navigation link info created.</returns>
        internal static ODataJsonLightReaderNavigationLinkInfo CreateSingletonEntityReferenceLinkInfo(
            ODataNavigationLink navigationLink,
            IEdmNavigationProperty navigationProperty,
            ODataEntityReferenceLink entityReferenceLink,
            bool isExpanded)
        {
            Debug.Assert(navigationLink != null, "navigationLink != null");
            Debug.Assert(navigationLink.IsCollection == false, "Singleton entity reference can only be reported for a singleton navigation links.");
            Debug.Assert(navigationProperty != null, "navigationProperty != null");

            ODataJsonLightReaderNavigationLinkInfo navigationLinkInfo = new ODataJsonLightReaderNavigationLinkInfo(navigationLink, navigationProperty, isExpanded);
            if (entityReferenceLink != null)
            {
                navigationLinkInfo.entityReferenceLinks = new LinkedList<ODataEntityReferenceLink>();
                navigationLinkInfo.entityReferenceLinks.AddFirst(entityReferenceLink);
            }

            return navigationLinkInfo;
        }
 protected override void EndNavigationLinkWithContent(ODataNavigationLink navigationLink)
 {
     throw new NotImplementedException();
 }
        /// <summary>
        /// Writes the navigation link's start element and atom metadata.
        /// </summary>
        /// <param name="navigationLink">The navigation link to write.</param>
        /// <param name="navigationLinkUrlOverride">Url to use for the navigation link. If this is specified the Url property on the <paramref name="navigationLink"/>
        /// will be ignored. If this parameter is null, the Url from the navigation link is used.</param>
        internal void WriteNavigationLinkStart(ODataNavigationLink navigationLink, Uri navigationLinkUrlOverride)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(navigationLink != null, "navigationLink != null");
            Debug.Assert(!string.IsNullOrEmpty(navigationLink.Name), "The navigation link name was not verified yet.");
            Debug.Assert(navigationLink.Url != null, "The navigation link Url was not verified yet.");
            Debug.Assert(navigationLink.IsCollection.HasValue, "navigationLink.IsCollection.HasValue");

            // <atom:link>
            this.XmlWriter.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomLinkElementName, AtomConstants.AtomNamespace);

            string linkRelation = AtomUtils.ComputeODataNavigationLinkRelation(navigationLink);
            string linkType = AtomUtils.ComputeODataNavigationLinkType(navigationLink);
            string linkTitle = navigationLink.Name;

            Uri navigationLinkUrl = navigationLinkUrlOverride ?? navigationLink.Url;
            AtomLinkMetadata linkMetadata = navigationLink.GetAnnotation<AtomLinkMetadata>();
            AtomLinkMetadata mergedMetadata = ODataAtomWriterMetadataUtils.MergeLinkMetadata(linkMetadata, linkRelation, navigationLinkUrl, linkTitle, linkType);
            this.atomEntryMetadataSerializer.WriteAtomLinkAttributes(mergedMetadata, null /* etag */);
        }
        /// <summary>
        /// Reads expanded feed navigation link.
        /// </summary>
        /// <param name="entryState">The state of the reader for entry to read.</param>
        /// <param name="navigationProperty">The navigation property for which to read the expanded link.</param>
        /// <returns>The navigation link info for the expanded link read.</returns>
        /// <remarks>
        /// This method doesn't move the reader.
        /// </remarks>
        private static ODataJsonLightReaderNavigationLinkInfo ReadExpandedFeedNavigationLink(IODataJsonLightReaderEntryState entryState, IEdmNavigationProperty navigationProperty)
        {
            Debug.Assert(entryState != null, "entryState != null");
            Debug.Assert(navigationProperty != null, "navigationProperty != null");

            ODataNavigationLink navigationLink = new ODataNavigationLink()
            {
                Name = navigationProperty.Name,
                IsCollection = true
            };

            ODataFeed expandedFeed = new ODataFeed();

            Dictionary<string, object> propertyAnnotations = entryState.DuplicatePropertyNamesChecker.GetODataPropertyAnnotations(navigationLink.Name);
            if (propertyAnnotations != null)
            {
                foreach (KeyValuePair<string, object> propertyAnnotation in propertyAnnotations)
                {
                    switch (propertyAnnotation.Key)
                    {
                        case ODataAnnotationNames.ODataNavigationLinkUrl:
                            Debug.Assert(propertyAnnotation.Value is Uri && propertyAnnotation.Value != null, "The odata.navigationLinkUrl annotation should have been parsed as a non-null Uri.");
                            navigationLink.Url = (Uri)propertyAnnotation.Value;
                            break;

                        case ODataAnnotationNames.ODataAssociationLinkUrl:
                            Debug.Assert(propertyAnnotation.Value is Uri && propertyAnnotation.Value != null, "The odata.associationLinkUrl annotation should have been parsed as a non-null Uri.");
                            navigationLink.AssociationLinkUrl = (Uri)propertyAnnotation.Value;
                            break;

                        case ODataAnnotationNames.ODataNextLink:
                            Debug.Assert(propertyAnnotation.Value is Uri && propertyAnnotation.Value != null, "The odata.nextLink annotation should have been parsed as a non-null Uri.");
                            expandedFeed.NextPageLink = (Uri)propertyAnnotation.Value;
                            break;

                        case ODataAnnotationNames.ODataCount:
                            Debug.Assert(propertyAnnotation.Value is long && propertyAnnotation.Value != null, "The odata.count annotation should have been parsed as a non-null long.");
                            expandedFeed.Count = (long?)propertyAnnotation.Value;
                            break;
                        case ODataAnnotationNames.ODataContext:
                            Debug.Assert(propertyAnnotation.Value is Uri && propertyAnnotation.Value != null, "The odata.context annotation should have been parsed as a non-null Uri.");
                            navigationLink.ContextUrl = (Uri)propertyAnnotation.Value;
                            break;

                        case ODataAnnotationNames.ODataDeltaLink:   // Delta links are not supported on expanded feeds.
                        default:
                            throw new ODataException(ODataErrorStrings.ODataJsonLightEntryAndFeedDeserializer_UnexpectedExpandedCollectionNavigationLinkPropertyAnnotation(navigationLink.Name, propertyAnnotation.Key));
                    }
                }
            }

            return ODataJsonLightReaderNavigationLinkInfo.CreateExpandedFeedLinkInfo(navigationLink, navigationProperty, expandedFeed);
        }
        /// <summary>
        /// Reads the information of a deferred link.
        /// </summary>
        /// <param name="entryState">The state of the reader for entry to read.</param>
        /// <param name="navigationPropertyName">The name of the navigation property for which to read the deferred link.</param>
        /// <param name="navigationProperty">The navigation property for which to read the deferred link. This can be null.</param>
        /// <returns>Returns the navigation link info for the deferred navigation link read.</returns>
        /// <remarks>
        /// This method doesn't move the reader.
        /// </remarks>
        private static ODataJsonLightReaderNavigationLinkInfo ReadDeferredNavigationLink(IODataJsonLightReaderEntryState entryState, string navigationPropertyName, IEdmNavigationProperty navigationProperty)
        {
            Debug.Assert(entryState != null, "entryState != null");
            Debug.Assert(!string.IsNullOrEmpty(navigationPropertyName), "!string.IsNullOrEmpty(navigationPropertyName)");
            Debug.Assert(navigationProperty == null || navigationPropertyName == navigationProperty.Name, "navigationProperty == null || navigationPropertyName == navigationProperty.Name");

            ODataNavigationLink navigationLink = new ODataNavigationLink()
            {
                Name = navigationPropertyName,
                IsCollection = navigationProperty == null ? null : (bool?)navigationProperty.Type.IsCollection()
            };

            Dictionary<string, object> propertyAnnotations = entryState.DuplicatePropertyNamesChecker.GetODataPropertyAnnotations(navigationLink.Name);
            if (propertyAnnotations != null)
            {
                foreach (KeyValuePair<string, object> propertyAnnotation in propertyAnnotations)
                {
                    switch (propertyAnnotation.Key)
                    {
                        case ODataAnnotationNames.ODataNavigationLinkUrl:
                            Debug.Assert(propertyAnnotation.Value is Uri && propertyAnnotation.Value != null, "The odata.navigationLinkUrl annotation should have been parsed as a non-null Uri.");
                            navigationLink.Url = (Uri)propertyAnnotation.Value;
                            break;

                        case ODataAnnotationNames.ODataAssociationLinkUrl:
                            Debug.Assert(propertyAnnotation.Value is Uri && propertyAnnotation.Value != null, "The odata.associationLinkUrl annotation should have been parsed as a non-null Uri.");
                            navigationLink.AssociationLinkUrl = (Uri)propertyAnnotation.Value;
                            break;

                        default:
                            throw new ODataException(ODataErrorStrings.ODataJsonLightEntryAndFeedDeserializer_UnexpectedDeferredLinkPropertyAnnotation(navigationLink.Name, propertyAnnotation.Key));
                    }
                }
            }

            return ODataJsonLightReaderNavigationLinkInfo.CreateDeferredLinkInfo(navigationLink, navigationProperty);
        }
Beispiel #19
0
        /// <summary>
        /// Start writing a navigation link with content.
        /// </summary>
        /// <param name="navigationLink">The navigation link to write.</param>
        protected override void StartNavigationLinkWithContent(ODataNavigationLink navigationLink)
        {
            Debug.Assert(navigationLink != null, "navigationLink != null");

            // In requests, a navigation link can have multiple items in its content (in the OM view), either entity reference links or expanded entry/feed.
            // For each of these we need to write a separate atom:link element. So we can't write the start of the atom:link element here
            // instead we postpone writing it till the first item in the content.
            // In response, only one item can occur, but for simplicity we will keep the behavior the same as for request and thus postpone writing the atom:link
            // start element as well.
            // Note that the writer core guarantees that this method (and the matching EndNavigationLinkWithContent) is only called for navigation links
            // which actually have some content. The only case where navigation link doesn't have a content is in response, in which case this method won't
            // be called, instead the WriteDeferredNavigationLink is called.
        }
Beispiel #20
0
        /// <summary>
        /// Start writing a navigation link.
        /// </summary>
        /// <param name="navigationLink">The navigation link to write.</param>
        protected override void WriteDeferredNavigationLink(ODataNavigationLink navigationLink)
        {
            Debug.Assert(navigationLink != null, "navigationLink != null");
            Debug.Assert(this.atomOutputContext.WritingResponse, "Deferred links are only supported in response, we should have verified this already.");

            this.WriteNavigationLinkStart(navigationLink, null);
            this.WriteNavigationLinkEnd();
        }
        /// <summary>
        /// Creates a navigation link info for a projected navigation link that is missing from the payload.
        /// </summary>
        /// <param name="navigationProperty">The navigation property for which the link will be reported.</param>
        /// <returns>The navigation link info created.</returns>
        internal static ODataJsonLightReaderNavigationLinkInfo CreateProjectedNavigationLinkInfo(IEdmNavigationProperty navigationProperty)
        {
            Debug.Assert(navigationProperty != null, "navigationProperty != null");

            ODataNavigationLink navigationLink = new ODataNavigationLink { Name = navigationProperty.Name, IsCollection = navigationProperty.Type.IsCollection() };
            ODataJsonLightReaderNavigationLinkInfo navigationLinkInfo = new ODataJsonLightReaderNavigationLinkInfo(navigationLink, navigationProperty, /*isExpanded*/ false);
            return navigationLinkInfo;
        }
        /// <summary>
        /// Creates a navigation link info for a collection of entity reference links.
        /// </summary>
        /// <param name="navigationLink">The navigation link to report.</param>
        /// <param name="navigationProperty">The navigation property for which the link will be reported.</param>
        /// <param name="entityReferenceLinks">The entity reference links for the navigation link to report.</param>
        /// <param name="isExpanded">true if the navigation link is expanded.</param>
        /// <returns>The navigation link info created.</returns>
        internal static ODataJsonLightReaderNavigationLinkInfo CreateCollectionEntityReferenceLinksInfo(
            ODataNavigationLink navigationLink,
            IEdmNavigationProperty navigationProperty,
            LinkedList<ODataEntityReferenceLink> entityReferenceLinks,
            bool isExpanded)
        {
            Debug.Assert(navigationLink != null, "navigationLink != null");
            Debug.Assert(navigationLink.IsCollection == true, "Collection entity reference can only be reported for a collection navigation links.");
            Debug.Assert(navigationProperty != null, "navigationProperty != null");
            Debug.Assert(entityReferenceLinks == null || entityReferenceLinks.Count > 0, "entityReferenceLinks == null || entityReferenceLinks.Count > 0");

            ODataJsonLightReaderNavigationLinkInfo navigationLinkInfo = new ODataJsonLightReaderNavigationLinkInfo(navigationLink, navigationProperty, isExpanded);
            navigationLinkInfo.entityReferenceLinks = entityReferenceLinks;
            return navigationLinkInfo;
        }
        /// <summary>
        /// Reads expanded entry navigation link.
        /// </summary>
        /// <param name="entryState">The state of the reader for entry to read.</param>
        /// <param name="navigationProperty">The navigation property for which to read the expanded link.</param>
        /// <returns>The navigation link info for the expanded link read.</returns>
        /// <remarks>
        /// This method doesn't move the reader.
        /// </remarks>
        private static ODataJsonLightReaderNavigationLinkInfo ReadExpandedEntryNavigationLink(IODataJsonLightReaderEntryState entryState, IEdmNavigationProperty navigationProperty)
        {
            Debug.Assert(entryState != null, "entryState != null");
            Debug.Assert(navigationProperty != null, "navigationProperty != null");

            ODataNavigationLink navigationLink = new ODataNavigationLink()
            {
                Name = navigationProperty.Name,
                IsCollection = false
            };

            Dictionary<string, object> propertyAnnotations = entryState.DuplicatePropertyNamesChecker.GetODataPropertyAnnotations(navigationLink.Name);
            if (propertyAnnotations != null)
            {
                foreach (KeyValuePair<string, object> propertyAnnotation in propertyAnnotations)
                {
                    switch (propertyAnnotation.Key)
                    {
                        case ODataAnnotationNames.ODataNavigationLinkUrl:
                            Debug.Assert(propertyAnnotation.Value is Uri && propertyAnnotation.Value != null, "The odata.navigationLinkUrl annotation should have been parsed as a non-null Uri.");
                            navigationLink.Url = (Uri)propertyAnnotation.Value;
                            break;

                        case ODataAnnotationNames.ODataAssociationLinkUrl:
                            Debug.Assert(propertyAnnotation.Value is Uri && propertyAnnotation.Value != null, "The odata.associationLinkUrl annotation should have been parsed as a non-null Uri.");
                            navigationLink.AssociationLinkUrl = (Uri)propertyAnnotation.Value;
                            break;

                        case ODataAnnotationNames.ODataContext:
                            Debug.Assert(propertyAnnotation.Value is Uri && propertyAnnotation.Value != null, "The odata.context annotation should have been parsed as a non-null Uri.");
                            navigationLink.ContextUrl = (Uri)propertyAnnotation.Value;
                            break;

                        default:
                            throw new ODataException(ODataErrorStrings.ODataJsonLightEntryAndFeedDeserializer_UnexpectedExpandedSingletonNavigationLinkPropertyAnnotation(navigationLink.Name, propertyAnnotation.Key));
                    }
                }
            }

            return ODataJsonLightReaderNavigationLinkInfo.CreateExpandedEntryLinkInfo(navigationLink, navigationProperty);
        }
Beispiel #24
0
        /// <summary>
        /// Write an entity reference link.
        /// </summary>
        /// <param name="parentNavigationLink">The parent navigation link which is being written around the entity reference link.</param>
        /// <param name="entityReferenceLink">The entity reference link to write.</param>
        protected override void WriteEntityReferenceInNavigationLinkContent(ODataNavigationLink parentNavigationLink, ODataEntityReferenceLink entityReferenceLink)
        {
            Debug.Assert(parentNavigationLink != null, "parentNavigationLink != null");
            Debug.Assert(entityReferenceLink != null, "entityReferenceLink != null");
            Debug.Assert(entityReferenceLink.Url != null, "We should have already verifies that the Url specified on the entity reference link is not null.");

            this.WriteNavigationLinkStart(parentNavigationLink, entityReferenceLink.Url);
            this.WriteNavigationLinkEnd();
        }
        /// <summary>
        /// Reads entity reference links for a collection navigation link in request.
        /// </summary>
        /// <param name="entryState">The state of the reader for entry to read.</param>
        /// <param name="navigationProperty">The navigation property for which to read the entity reference links.</param>
        /// <param name="isExpanded">true if the navigation link is expanded.</param>
        /// <returns>The navigation link info for the entity reference links read.</returns>
        /// <remarks>
        /// This method doesn't move the reader.
        /// </remarks>
        private static ODataJsonLightReaderNavigationLinkInfo ReadEntityReferenceLinksForCollectionNavigationLinkInRequest(
            IODataJsonLightReaderEntryState entryState,
            IEdmNavigationProperty navigationProperty,
            bool isExpanded)
        {
            Debug.Assert(entryState != null, "entryState != null");
            Debug.Assert(navigationProperty != null, "navigationProperty != null");

            ODataNavigationLink navigationLink = new ODataNavigationLink()
            {
                Name = navigationProperty.Name,
                IsCollection = true
            };

            Dictionary<string, object> propertyAnnotations = entryState.DuplicatePropertyNamesChecker.GetODataPropertyAnnotations(navigationLink.Name);
            LinkedList<ODataEntityReferenceLink> entityReferenceLinksList = null;
            if (propertyAnnotations != null)
            {
                foreach (KeyValuePair<string, object> propertyAnnotation in propertyAnnotations)
                {
                    switch (propertyAnnotation.Key)
                    {
                        case ODataAnnotationNames.ODataBind:
                            ODataEntityReferenceLink entityReferenceLink = propertyAnnotation.Value as ODataEntityReferenceLink;
                            if (entityReferenceLink != null)
                            {
                                throw new ODataException(ODataErrorStrings.ODataJsonLightEntryAndFeedDeserializer_StringValueForCollectionBindPropertyAnnotation(navigationLink.Name, ODataAnnotationNames.ODataBind));
                            }

                            Debug.Assert(
                                propertyAnnotation.Value is LinkedList<ODataEntityReferenceLink> && propertyAnnotation.Value != null,
                                "The value of odata.bind property annotation must be either ODataEntityReferenceLink or List<ODataEntityReferenceLink>");
                            entityReferenceLinksList = (LinkedList<ODataEntityReferenceLink>)propertyAnnotation.Value;
                            break;

                        default:
                            throw new ODataException(ODataErrorStrings.ODataJsonLightEntryAndFeedDeserializer_UnexpectedNavigationLinkInRequestPropertyAnnotation(
                                navigationLink.Name,
                                propertyAnnotation.Key,
                                ODataAnnotationNames.ODataBind));
                    }
                }
            }

            return ODataJsonLightReaderNavigationLinkInfo.CreateCollectionEntityReferenceLinksInfo(navigationLink, navigationProperty, entityReferenceLinksList, isExpanded);
        }
Beispiel #26
0
 /// <summary>
 /// Writes the navigation link's start element and atom metadata.
 /// </summary>
 /// <param name="navigationLink">The navigation link to write.</param>
 /// <param name="navigationLinkUrlOverride">Url to use for the navigation link. If this is specified the Url property on the <paramref name="navigationLink"/>
 /// will be ignored. If this parameter is null, the Url from the navigation link is used.</param>
 private void WriteNavigationLinkStart(ODataNavigationLink navigationLink, Uri navigationLinkUrlOverride)
 {
     WriterValidationUtils.ValidateNavigationLinkHasCardinality(navigationLink);
     WriterValidationUtils.ValidateNavigationLinkUrlPresent(navigationLink);
     this.atomEntryAndFeedSerializer.WriteNavigationLinkStart(navigationLink, navigationLinkUrlOverride);
 }
 protected override void WriteDeferredNavigationLink(ODataNavigationLink navigationLink)
 {
     throw new NotImplementedException();
 }
        /// <summary>
        /// Start writing a deferred (non-expanded) navigation link.
        /// </summary>
        /// <param name="navigationLink">The navigation link to write.</param>
        protected override void WriteDeferredNavigationLink(ODataNavigationLink navigationLink)
        {
            Debug.Assert(navigationLink != null, "navigationLink != null");
            Debug.Assert(this.jsonLightOutputContext.WritingResponse, "Deferred links are only supported in response, we should have verified this already.");

            // A deferred navigation link is just the link metadata, no value.
            this.jsonLightEntryAndFeedSerializer.WriteNavigationLinkMetadata(navigationLink, this.DuplicatePropertyNamesChecker);
        }
 protected override void WriteEntityReferenceInNavigationLinkContent(ODataNavigationLink parentNavigationLink, ODataEntityReferenceLink entityReferenceLink)
 {
     throw new NotImplementedException();
 }
        /// <summary>
        /// Start writing a navigation link with content.
        /// </summary>
        /// <param name="navigationLink">The navigation link to write.</param>
        protected override void StartNavigationLinkWithContent(ODataNavigationLink navigationLink)
        {
            Debug.Assert(navigationLink != null, "navigationLink != null");
            Debug.Assert(!string.IsNullOrEmpty(navigationLink.Name), "The navigation link name should have been verified by now.");

            if (this.jsonLightOutputContext.WritingResponse)
            {
                // Write @odata.context annotation for navigation property
                var containedEntitySet = this.CurrentScope.NavigationSource as IEdmContainedEntitySet;
                if (containedEntitySet != null)
                {
                    ODataContextUrlInfo info = ODataContextUrlInfo.Create(
                                                this.CurrentScope.NavigationSource,
                                                this.CurrentScope.EntityType.FullName(),
                                                containedEntitySet.NavigationProperty.Type.TypeKind() != EdmTypeKind.Collection,
                                                this.CurrentScope.ODataUri);
                    this.jsonLightEntryAndFeedSerializer.WriteNavigationLinkContextUrl(navigationLink, info);
                }

                // Write the navigation link metadata first. The rest is written by the content entry or feed.
                this.jsonLightEntryAndFeedSerializer.WriteNavigationLinkMetadata(navigationLink, this.DuplicatePropertyNamesChecker);
            }
            else
            {
                WriterValidationUtils.ValidateNavigationLinkHasCardinality(navigationLink);
            }
        }