/// <summary> /// Writes the self or edit link. /// </summary> /// <param name="writer">The Xml writer to write to.</param> /// <param name="baseUri">The base Uri of the document or null if none was specified.</param> /// <param name="link">Uri object for the link.</param> /// <param name="linkRelation">Relationship value. Either "edit" or "self".</param> /// <param name="title">Title for the link.</param> internal static void WriteReadOrEditLink(XmlWriter writer, Uri baseUri, Uri link, string linkRelation, string title) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(writer != null, "writer != null"); if (link != null) { // <atom:link> writer.WriteStartElement( AtomConstants.AtomNamespacePrefix, AtomConstants.AtomLinkElementName, AtomConstants.AtomNamespace); writer.WriteAttributeString( AtomConstants.AtomLinkRelationAttributeName, linkRelation); writer.WriteAttributeString( AtomConstants.AtomTitleElementName, title ?? String.Empty); writer.WriteAttributeString( AtomConstants.AtomHRefAttributeName, AtomUtils.ToUrlAttributeValue(link, baseUri)); // </atom:link> writer.WriteEndElement(); } }
/// <summary> /// Write the metadata for an OData association link; makes sure any duplicate of the link's values duplicated in metadata are equal. /// </summary> /// <param name="writer">The Xml writer to write to.</param> /// <param name="baseUri">The base Uri of the document or null if none was specified.</param> /// <param name="entry">The entry for which to write the association link.</param> /// <param name="associationLink">The association link for which to write the metadata.</param> internal static void WriteODataAssociationLinkMetadata(XmlWriter writer, Uri baseUri, ODataEntry entry, ODataAssociationLink associationLink) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(writer != null, "writer != null"); Debug.Assert(entry != null, "entry != null"); Debug.Assert(associationLink != null, "link != null"); Debug.Assert(!string.IsNullOrEmpty(associationLink.Name), "The link name was not verified yet."); Debug.Assert(associationLink.Url != null, "The link Url was not verified yet."); string linkRelation = AtomUtils.ComputeODataAssociationLinkRelation(associationLink); string linkHref = AtomUtils.ToUrlAttributeValue(associationLink.Url, baseUri); string linkHrefLang = null; string linkType = MimeConstants.MimeApplicationXml; string linkTitle = associationLink.Name; int? linkLength = null; AtomLinkMetadata linkMetadata = null; AtomEntryMetadata entryMetadata = entry.Atom(); if (entryMetadata != null) { // TODO: Determine the link metadata from the entry } if (linkMetadata != null) { Uri metadataHref = linkMetadata.Href; if (metadataHref != null) { string metadataHrefString = AtomUtils.ToUrlAttributeValue(metadataHref, baseUri); if (metadataHrefString != linkHref) { throw new ODataException(Strings.ODataAtomWriter_LinkMetadataHrefMustBeEqualWithLinkUrl(metadataHrefString, linkHref)); } } string metadataRelation = linkMetadata.Relation; if (metadataRelation != null && metadataRelation != linkRelation) { throw new ODataException(Strings.ODataAtomWriter_LinkMetadataRelationMustBeEqualWithComputedRelation(metadataRelation, linkRelation)); } string metadataType = linkMetadata.MediaType; if (metadataType != null && metadataType != linkType) { throw new ODataException(Strings.ODataAtomWriter_LinkMetadataMediaTypeMustBeEqualWithComputedType(metadataRelation, linkType)); } string metadataTitle = linkMetadata.Title; if (metadataTitle != null && metadataTitle != linkTitle) { throw new ODataException(Strings.ODataAtomWriter_LinkMetadataTitleMustBeEqualWithLinkName(metadataTitle, linkTitle)); } linkHrefLang = linkMetadata.HrefLang; linkLength = linkMetadata.Length; } WriteAtomLinkMetadataAttributes(writer, linkRelation, linkHref, linkHrefLang, linkTitle, linkType, linkLength); }
/// <summary> /// Writes the specified start/end tags and the specified person metadata as content /// </summary> /// <param name="writer">The Xml writer to write to.</param> /// <param name="baseUri">The base Uri of the document or null if none was specified.</param> /// <param name="personMetadata">The person metadata to write.</param> internal static void WritePersonMetadata(XmlWriter writer, Uri baseUri, AtomPersonMetadata personMetadata) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(writer != null, "writer != null"); Debug.Assert(personMetadata != null, "Person metadata must not be null."); // <atom:name>name of person</atom:name> // NOTE: write an empty element if no name is specified because the element is required. ODataAtomWriterUtils.WriteElementWithTextContent(writer, AtomConstants.AtomNamespacePrefix, AtomConstants.AtomPersonNameElementName, AtomConstants.AtomNamespace, personMetadata.Name); string uriString = personMetadata.UriFromEpm; if (uriString != null) { Debug.Assert( personMetadata.Uri == null, "If the internal UriFromEpm was used, then the Uri property must be left null. The merge between custom and EPM is probably wrong."); } else { Uri uri = personMetadata.Uri; if (uri != null) { uriString = AtomUtils.ToUrlAttributeValue(uri, baseUri); } } if (uriString != null) { ODataAtomWriterUtils.WriteElementWithTextContent( writer, AtomConstants.AtomNamespacePrefix, AtomConstants.AtomPersonUriElementName, AtomConstants.AtomNamespace, uriString); } string email = personMetadata.Email; if (email != null) { ODataAtomWriterUtils.WriteElementWithTextContent( writer, AtomConstants.AtomNamespacePrefix, AtomConstants.AtomPersonEmailElementName, AtomConstants.AtomNamespace, email); } }
/// <summary> /// Write the metadata of a link in ATOM format /// </summary> /// <param name="writer">The Xml writer to write to.</param> /// <param name="baseUri">The base Uri of the document or null if none was specified.</param> /// <param name="linkMetadata">The link metadata to write.</param> internal static void WriteAtomLinkMetadata(XmlWriter writer, Uri baseUri, AtomLinkMetadata linkMetadata) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(writer != null, "writer != null"); Debug.Assert(linkMetadata != null, "Link metadata must not be null."); // <atom:link ... > writer.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomLinkElementName, AtomConstants.AtomNamespace); string linkHref = linkMetadata.Href == null ? null : AtomUtils.ToUrlAttributeValue(linkMetadata.Href, baseUri); WriteAtomLinkMetadataAttributes(writer, linkMetadata.Relation, linkHref, linkMetadata.HrefLang, linkMetadata.Title, linkMetadata.MediaType, linkMetadata.Length); // </atom:link> writer.WriteEndElement(); }
/// <summary> /// Write the ATOM metadata for an entry /// </summary> /// <param name="writer">The Xml writer to write to.</param> /// <param name="baseUri">The base Uri of the document or null if none was specified.</param> /// <param name="entry">The entry for which to write the metadata.</param> /// <param name="epmEntryMetadata">The ATOM metadata for the entry which came from EPM.</param> internal static void WriteEntryMetadata(XmlWriter writer, Uri baseUri, ODataEntry entry, AtomEntryMetadata epmEntryMetadata) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(writer != null, "writer != null"); // TODO, ckerer: implement the rule around authors (an entry has to have an author directly or in the <entry:source> unless the feed has an author). // currently we make all entries have an author. AtomEntryMetadata customEntryMetadata = entry.GetAnnotation <AtomEntryMetadata>(); AtomEntryMetadata entryMetadata = ODataAtomWriterMetadataEpmMergeUtils.MergeCustomAndEpmEntryMetadata(customEntryMetadata, epmEntryMetadata); if (entryMetadata == null) { // write all required metadata elements with default content // <atom:title></atom:title> ODataAtomWriterUtils.WriteEmptyElement(writer, AtomConstants.AtomNamespacePrefix, AtomConstants.AtomTitleElementName, AtomConstants.AtomNamespace); // <atom:updated>dateTimeOffset</atom:updated> // NOTE: the <updated> element is required and if not specified the best we can do is to create a default // one with the current date/time. ODataAtomWriterUtils.WriteElementWithTextContent(writer, AtomConstants.AtomNamespacePrefix, AtomConstants.AtomUpdatedElementName, AtomConstants.AtomNamespace, ODataAtomConvert.ToString(DateTimeOffset.UtcNow)); WriteEmptyAuthor(writer); } else { // <atom:title>text</atom:title> // NOTE: writes an empty element even if no title was specified since the title is required ODataAtomWriterMetadataUtils.WriteTextConstruct(writer, AtomConstants.AtomNamespacePrefix, AtomConstants.AtomTitleElementName, AtomConstants.AtomNamespace, entryMetadata.Title); AtomTextConstruct summary = entryMetadata.Summary; if (summary != null) { // <atom:summary>text</atom:summary> ODataAtomWriterMetadataUtils.WriteTextConstruct(writer, AtomConstants.AtomNamespacePrefix, AtomConstants.AtomSummaryElementName, AtomConstants.AtomNamespace, summary); } DateTimeOffset?published = entryMetadata.Published; if (published.HasValue) { // <atom:published>dateTimeOffset</atom:published> ODataAtomWriterUtils.WriteElementWithTextContent(writer, AtomConstants.AtomNamespacePrefix, AtomConstants.AtomPublishedElementName, AtomConstants.AtomNamespace, ODataAtomConvert.ToString(published.Value)); } // <atom:updated>date</atom:updated> // NOTE: the <updated> element is required and if not specified the best we can do is to create a default // one with the current date/time. DateTimeOffset updated = entryMetadata.Updated.HasValue ? entryMetadata.Updated.Value : DateTimeOffset.UtcNow; ODataAtomWriterUtils.WriteElementWithTextContent( writer, AtomConstants.AtomNamespacePrefix, AtomConstants.AtomUpdatedElementName, AtomConstants.AtomNamespace, ODataAtomConvert.ToString(updated)); bool wroteAuthor = false; IEnumerable <AtomPersonMetadata> authors = entryMetadata.Authors; if (authors != null) { foreach (AtomPersonMetadata author in authors) { if (author == null) { throw new ODataException(Strings.ODataAtomWriterMetadataUtils_AuthorMetadataMustNotContainNull); } // <atom:author>author data</atom:author> writer.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomAuthorElementName, AtomConstants.AtomNamespace); WritePersonMetadata(writer, baseUri, author); writer.WriteEndElement(); wroteAuthor = true; } } if (!wroteAuthor) { // write empty authors since they are required WriteEmptyAuthor(writer); } IEnumerable <AtomPersonMetadata> contributors = entryMetadata.Contributors; if (contributors != null) { foreach (AtomPersonMetadata contributor in contributors) { if (contributor == null) { throw new ODataException(Strings.ODataAtomWriterMetadataUtils_ContributorMetadataMustNotContainNull); } // <atom:contributor>contributor data</atom:contributor> writer.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomContributorElementName, AtomConstants.AtomNamespace); WritePersonMetadata(writer, baseUri, contributor); writer.WriteEndElement(); } } IEnumerable <AtomLinkMetadata> links = entryMetadata.Links; if (links != null) { foreach (AtomLinkMetadata link in links) { if (link == null) { throw new ODataException(Strings.ODataAtomWriterMetadataUtils_LinkMetadataMustNotContainNull); } // <atom:link>...</atom:link> WriteAtomLinkMetadata(writer, baseUri, link); } } IEnumerable <AtomCategoryMetadata> categories = entryMetadata.Categories; if (categories != null) { foreach (AtomCategoryMetadata category in categories) { if (category == null) { throw new ODataException(Strings.ODataAtomWriterMetadataUtils_CategoryMetadataMustNotContainNull); } // <atom:category term="..." scheme="..." label="..."></atom:category> WriteCategory(writer, category); } } if (entryMetadata.Rights != null) { // <atom:rights>rights</atom:rights> ODataAtomWriterMetadataUtils.WriteTextConstruct(writer, AtomConstants.AtomNamespacePrefix, AtomConstants.AtomRightsElementName, AtomConstants.AtomNamespace, entryMetadata.Rights); } Uri icon = entryMetadata.Icon; if (icon != null) { // <atom:icon>Uri</atom:icon> ODataAtomWriterUtils.WriteElementWithTextContent( writer, AtomConstants.AtomNamespacePrefix, AtomConstants.AtomIconElementName, AtomConstants.AtomNamespace, AtomUtils.ToUrlAttributeValue(icon, baseUri)); } AtomFeedMetadata source = entryMetadata.Source; if (source != null) { // <atom:source> writer.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomSourceElementName, AtomConstants.AtomNamespace); WriteFeedMetadata(writer, baseUri, source, null); // </atom:source> writer.WriteEndElement(); } } }
/// <summary> /// Write the given feed metadata in atom format /// </summary> /// <param name="writer">The Xml writer to write to.</param> /// <param name="baseUri">The base Uri of the document or null if none was specified.</param> /// <param name="feedMetadata">The metadata to write.</param> /// <param name="feed">The feed for which to write the meadata or null if it is the metadata of an atom:source element.</param> private static void WriteFeedMetadata(XmlWriter writer, Uri baseUri, AtomFeedMetadata feedMetadata, ODataFeed feed) { Debug.Assert(writer != null, "writer != null"); Debug.Assert(feedMetadata != null, "Feed metadata must not be null!"); // <atom:id>text</atom:id> // NOTE: this is the Id of the feed. For a regular feed this is stored on the feed itself; // if used in the context of an <atom:source> element it is stored in metadata Debug.Assert(feed == null || !string.IsNullOrEmpty(feed.Id), "The feed Id should have been validated by now."); string id = feed == null ? feedMetadata.SourceId : feed.Id; ODataAtomWriterUtils.WriteElementWithTextContent( writer, AtomConstants.AtomNamespacePrefix, AtomConstants.AtomIdElementName, AtomConstants.AtomNamespace, id); // <atom:title>text</atom:title> // NOTE: write an empty element if no title is specified since the element is required ODataAtomWriterMetadataUtils.WriteTextConstruct(writer, AtomConstants.AtomNamespacePrefix, AtomConstants.AtomTitleElementName, AtomConstants.AtomNamespace, feedMetadata.Title); if (feedMetadata.Subtitle != null) { // <atom:subtitle>text</atom:subtitle> ODataAtomWriterMetadataUtils.WriteTextConstruct(writer, AtomConstants.AtomNamespacePrefix, AtomConstants.AtomSubtitleElementName, AtomConstants.AtomNamespace, feedMetadata.Subtitle); } // <atom:updated>date</atom:updated> // NOTE: the <updated> element is required and if not specified the best we can do is to create a default // one with the current date/time. DateTimeOffset updated = feedMetadata.Updated.HasValue ? feedMetadata.Updated.Value : DateTimeOffset.UtcNow; ODataAtomWriterUtils.WriteElementWithTextContent( writer, AtomConstants.AtomNamespacePrefix, AtomConstants.AtomUpdatedElementName, AtomConstants.AtomNamespace, ODataAtomConvert.ToString(updated)); IEnumerable <AtomPersonMetadata> authors = feedMetadata.Authors; if (authors != null) { foreach (AtomPersonMetadata author in authors) { // <atom:author>author data</atom:author> writer.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomAuthorElementName, AtomConstants.AtomNamespace); WritePersonMetadata(writer, baseUri, author); writer.WriteEndElement(); } } IEnumerable <AtomLinkMetadata> links = feedMetadata.Links; if (links != null) { foreach (AtomLinkMetadata link in links) { // <atom:link>...</atom:link> WriteAtomLinkMetadata(writer, baseUri, link); } } IEnumerable <AtomCategoryMetadata> categories = feedMetadata.Categories; if (categories != null) { foreach (AtomCategoryMetadata category in categories) { // <atom:category term="..." scheme="..." label="..."></atom:category> WriteCategory(writer, category); } } Uri logo = feedMetadata.Logo; if (logo != null) { // <atom:logo>Uri</atom:logo> ODataAtomWriterUtils.WriteElementWithTextContent( writer, AtomConstants.AtomNamespacePrefix, AtomConstants.AtomLogoElementName, AtomConstants.AtomNamespace, AtomUtils.ToUrlAttributeValue(logo, baseUri)); } if (feedMetadata.Rights != null) { // <atom:rights>rights</atom:rights> ODataAtomWriterMetadataUtils.WriteTextConstruct( writer, AtomConstants.AtomNamespacePrefix, AtomConstants.AtomRightsElementName, AtomConstants.AtomNamespace, feedMetadata.Rights); } IEnumerable <AtomPersonMetadata> contributors = feedMetadata.Contributors; if (contributors != null) { foreach (AtomPersonMetadata contributor in contributors) { // <atom:contributor>contributor data</atom:contributor> writer.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomContributorElementName, AtomConstants.AtomNamespace); WritePersonMetadata(writer, baseUri, contributor); writer.WriteEndElement(); } } AtomGeneratorMetadata generator = feedMetadata.Generator; if (generator != null) { // <atom:generator uri="..." version="...">name</atom:generator> writer.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomGeneratorElementName, AtomConstants.AtomNamespace); if (generator.Uri != null) { writer.WriteAttributeString(AtomConstants.AtomGeneratorUriAttributeName, AtomUtils.ToUrlAttributeValue(generator.Uri, baseUri)); } if (!string.IsNullOrEmpty(generator.Version)) { writer.WriteAttributeString(AtomConstants.AtomGeneratorVersionAttributeName, generator.Version); } writer.WriteString(generator.Name); writer.WriteEndElement(); } Uri icon = feedMetadata.Icon; if (icon != null) { // <atom:icon>Uri</atom:icon> ODataAtomWriterUtils.WriteElementWithTextContent( writer, AtomConstants.AtomNamespacePrefix, AtomConstants.AtomIconElementName, AtomConstants.AtomNamespace, AtomUtils.ToUrlAttributeValue(icon, baseUri)); } }
/// <summary> /// Writes a named stream to the ATOM payload including an /// </summary> /// <param name="writer">The Xml writer to write to.</param> /// <param name="baseUri">The base Uri of the document or null if none was specified.</param> /// <param name="namedStream">The named stream to create the payload for.</param> internal static void WriteNamedStream(XmlWriter writer, Uri baseUri, ODataMediaResource namedStream) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(writer != null, "writer != null"); Debug.Assert(namedStream != null, "Named stream must not be null."); // <atom:link rel="...2007/08/dataservices/mediaresource/name"> writer.WriteStartElement( AtomConstants.AtomNamespacePrefix, AtomConstants.AtomLinkElementName, AtomConstants.AtomNamespace); writer.WriteAttributeString( AtomConstants.AtomLinkRelationAttributeName, AtomUtils.ComputeNamedStreamRelation(namedStream, false)); writer.WriteAttributeString( AtomConstants.AtomTitleElementName, namedStream.Name); Uri readLink = namedStream.ReadLink; if (readLink != null) { // TODO, ckerer: should we throw when no source link is provided? writer.WriteAttributeString( AtomConstants.AtomHRefAttributeName, AtomUtils.ToUrlAttributeValue(readLink, baseUri)); } writer.WriteAttributeString( AtomConstants.AtomTypeAttributeName, namedStream.ContentType); writer.WriteEndElement(); if (namedStream.EditLink != null) { // <atom:link rel="...2007/08/dataservices/edit-media/name"> writer.WriteStartElement( AtomConstants.AtomNamespacePrefix, AtomConstants.AtomLinkElementName, AtomConstants.AtomNamespace); writer.WriteAttributeString( AtomConstants.AtomLinkRelationAttributeName, AtomUtils.ComputeNamedStreamRelation(namedStream, true)); writer.WriteAttributeString( AtomConstants.AtomTitleElementName, namedStream.Name); Uri editLink = namedStream.EditLink; if (editLink != null) { writer.WriteAttributeString( AtomConstants.AtomHRefAttributeName, AtomUtils.ToUrlAttributeValue(editLink, baseUri)); } writer.WriteAttributeString( AtomConstants.AtomTypeAttributeName, namedStream.ContentType); string etag = namedStream.ETag; if (etag != null) { WriteETag(writer, etag); } writer.WriteEndElement(); } }
/// <summary> /// Write the content of the given entry. /// </summary> /// <param name="entry">The entry for which to write properties.</param> /// <param name="entryType">The <see cref="ResourceType"/> of the entry (or null if not metadata is available).</param> /// <param name="propertiesValueCache">The cache of properties.</param> /// <param name="rootSourcePathSegment">The root of the EPM source tree, if there's an EPM applied.</param> private void WriteEntryContent(ODataEntry entry, ResourceType entryType, EntryPropertiesValueCache propertiesValueCache, EpmSourcePathSegment rootSourcePathSegment) { Debug.Assert(entry != null, "entry != null"); Debug.Assert(propertiesValueCache != null, "propertiesValueCache != null"); ODataMediaResource mediaResource = entry.MediaResource; if (mediaResource == null) { // <content type="application/xml"> this.writer.WriteStartElement( AtomConstants.AtomNamespacePrefix, AtomConstants.AtomContentElementName, AtomConstants.AtomNamespace); this.writer.WriteAttributeString( AtomConstants.AtomTypeAttributeName, MimeConstants.MimeApplicationXml); // <m:properties> // we always write the <m:properties> element even if there are no properties this.writer.WriteStartElement( AtomConstants.ODataMetadataNamespacePrefix, AtomConstants.AtomPropertiesElementName, AtomConstants.ODataMetadataNamespace); ODataAtomWriterUtils.WriteProperties( this.writer, this.MetadataProvider, entryType, propertiesValueCache.EntryProperties, this.Version, false, propertiesValueCache, rootSourcePathSegment); // </m:properties> this.writer.WriteEndElement(); // </content> this.writer.WriteEndElement(); } else { Uri mediaEditLink = mediaResource.EditLink; if (mediaEditLink != null) { // <link rel="edit-media" href="href" /> this.writer.WriteStartElement( AtomConstants.AtomNamespacePrefix, AtomConstants.AtomLinkElementName, AtomConstants.AtomNamespace); this.writer.WriteAttributeString( AtomConstants.AtomLinkRelationAttributeName, AtomConstants.AtomEditMediaRelationAttributeValue); this.writer.WriteAttributeString( AtomConstants.AtomHRefAttributeName, AtomUtils.ToUrlAttributeValue(mediaEditLink, this.BaseUri)); string mediaETag = mediaResource.ETag; if (mediaETag != null) { this.writer.WriteAttributeString( AtomConstants.ODataMetadataNamespacePrefix, AtomConstants.ODataETagAttributeName, AtomConstants.ODataMetadataNamespace, mediaETag); } // </link> this.writer.WriteEndElement(); } Debug.Assert(mediaEditLink != null || mediaResource.ETag == null, "The default stream edit link and etag should have been validated by now."); // <content type="type" src="src"> this.writer.WriteStartElement( AtomConstants.AtomNamespacePrefix, AtomConstants.AtomContentElementName, AtomConstants.AtomNamespace); Debug.Assert(!string.IsNullOrEmpty(mediaResource.ContentType), "The default stream content type should have been validated by now."); this.writer.WriteAttributeString( AtomConstants.AtomTypeAttributeName, mediaResource.ContentType); Debug.Assert(mediaResource.ReadLink != null, "The default stream read link should have been validated by now."); this.writer.WriteAttributeString( AtomConstants.MediaLinkEntryContentSourceAttributeName, AtomUtils.ToUrlAttributeValue(mediaResource.ReadLink, this.BaseUri)); // </content> this.writer.WriteEndElement(); // <m:properties> // we always write the <m:properties> element even if there are no properties this.writer.WriteStartElement( AtomConstants.ODataMetadataNamespacePrefix, AtomConstants.AtomPropertiesElementName, AtomConstants.ODataMetadataNamespace); ODataAtomWriterUtils.WriteProperties( this.writer, this.MetadataProvider, entryType, propertiesValueCache.EntryProperties, this.Version, false, propertiesValueCache, rootSourcePathSegment); // </m:properties> this.writer.WriteEndElement(); } }
/// <summary> /// Finish writing a feed. /// </summary> /// <param name="feed">The feed to write.</param> protected override void EndFeed(ODataFeed feed) { Debug.Assert(feed != null, "feed != null"); Uri nextPageLink = feed.NextPageLink; if (nextPageLink != null) { // <atom:link rel="next" href="next-page-link" /> this.writer.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomLinkElementName, AtomConstants.AtomNamespace); this.writer.WriteAttributeString(AtomConstants.AtomLinkRelationAttributeName, AtomConstants.AtomLinkRelationNextAttributeValue); this.writer.WriteAttributeString(AtomConstants.AtomHRefAttributeName, AtomUtils.ToUrlAttributeValue(nextPageLink, this.BaseUri)); this.writer.WriteEndElement(); } ODataAtomWriterMetadataUtils.WriteFeedMetadata(this.writer, this.BaseUri, feed); // </atom:feed> this.writer.WriteEndElement(); }