/// <summary> /// Gets an entity metadata builder for the given entry. /// </summary> /// <param name="entryState">Entry state to use as reference for information needed by the builder.</param> /// <returns>An entity metadata builder.</returns> public ODataEntityMetadataBuilder GetEntityMetadataBuilderForReader(IODataJsonLightReaderEntryState entryState) { Debug.Assert(entryState != null, "entry != null"); // Only apply the conventional template builder on response. On a request we would only report what's on the wire. if (entryState.MetadataBuilder == null) { ODataEntry entry = entryState.Entry; if (this.isResponse) { ODataTypeAnnotation typeAnnotation = entry.GetAnnotation <ODataTypeAnnotation>(); Debug.Assert(typeAnnotation != null, "The JSON light reader should have already set the ODataTypeAnnotation."); IEdmNavigationSource navigationSource = typeAnnotation.NavigationSource; IEdmEntityType navigationSourceElementType = this.edmTypeResolver.GetElementType(navigationSource); IODataFeedAndEntryTypeContext typeContext = ODataFeedAndEntryTypeContext.Create(/*serializationInfo*/ null, navigationSource, navigationSourceElementType, entryState.EntityType, this.model, /*throwIfMissingTypeInfo*/ true); IODataEntryMetadataContext entryMetadataContext = ODataEntryMetadataContext.Create(entry, typeContext, /*serializationInfo*/ null, (IEdmEntityType)entry.GetEdmType().Definition, this, entryState.SelectedProperties); UrlConvention urlConvention = UrlConvention.ForUserSettingAndTypeContext(/*keyAsSegment*/ null, typeContext); ODataConventionalUriBuilder uriBuilder = new ODataConventionalUriBuilder(this.ServiceBaseUri, urlConvention); entryState.MetadataBuilder = new ODataConventionalEntityMetadataBuilder(entryMetadataContext, this, uriBuilder); } else { entryState.MetadataBuilder = new NoOpEntityMetadataBuilder(entry); } } return(entryState.MetadataBuilder); }
/// <summary> /// Creates a new instance of MaterializerEntry. /// </summary> /// <param name="entry">The entry.</param> /// <param name="format">The format the entry was read in.</param> /// <param name="isTracking">True if the contents of the entry will be tracked in the context, otherwise False.</param> /// <param name="model">The client model.</param> private MaterializerEntry(ODataEntry entry, ODataFormat format, bool isTracking, ClientEdmModel model) { Debug.Assert(entry != null, "entry != null"); this.entry = entry; this.Format = format; this.entityDescriptor = new EntityDescriptor(model); this.isAtomOrTracking = isTracking || this.Format == ODataFormat.Atom; string serverTypeName = this.Entry.TypeName; SerializationTypeNameAnnotation serializationTypeNameAnnotation = entry.GetAnnotation <SerializationTypeNameAnnotation>(); if (serializationTypeNameAnnotation != null) { // If the annotation has a value use it. Otherwise, in JSON-Light, the types can be inferred from the // metadata URI even if they are not present on the wire, so just use the type name from the entry. if (serializationTypeNameAnnotation.TypeName != null || this.Format != ODataFormat.Json) { serverTypeName = serializationTypeNameAnnotation.TypeName; } } this.entityDescriptor.ServerTypeName = serverTypeName; }
private ResourceType GetEntryResourceType(ODataEntry entry, ResourceType expectedType) { ResourceType type; string typeName = entry.TypeName; SerializationTypeNameAnnotation annotation = entry.GetAnnotation <SerializationTypeNameAnnotation>(); if (annotation != null) { typeName = annotation.TypeName; } if (string.IsNullOrEmpty(typeName)) { type = expectedType; if (base.Service.Provider.HasDerivedTypes(type)) { throw DataServiceException.CreateBadRequestError(System.Data.Services.Strings.BadRequest_TypeInformationMustBeSpecifiedForInhertiance); } return(type); } type = base.Service.Provider.TryResolveResourceType(typeName); if (type == null) { throw DataServiceException.CreateBadRequestError(System.Data.Services.Strings.BadRequest_InvalidTypeName(typeName)); } if (!expectedType.IsAssignableFrom(type)) { throw DataServiceException.CreateBadRequestError(System.Data.Services.Strings.BadRequest_InvalidTypeSpecified(typeName, expectedType.FullName)); } return(type); }
/// <summary> /// Returns the new XmlWriter to cache the payload for firing WritingEntity event. /// </summary> /// <param name="entry">ODataEntry instance that is currently getting serialized.</param> /// <param name="entryWriter">XmlWriter that is used to write the payload.</param> /// <returns>XmlWriter instance that needs to be used to write the payload for the given odataentry.</returns> private static XmlWriter StartEntryXmlCustomizer(ODataEntry entry, XmlWriter entryWriter) { WritingEntityInfo writingEntityInfo = entry.GetAnnotation <WritingEntityInfo>(); Debug.Assert(writingEntityInfo.RequestInfo.HasWritingEventHandlers, "this.requestInfo.HasWritingEventHandlers"); return(writingEntityInfo.EntryPayload.CreateWriter()); }
/// <summary> /// Finish writing an entry. /// </summary> /// <param name="entry">The entry to write.</param> protected override void EndEntry(ODataEntry entry) { if (entry == null) { Debug.Assert( this.ParentNavigationLink != null && !this.ParentNavigationLink.IsCollection.Value, "when entry == null, it has to be and expanded single entry navigation"); // this is a null expanded single entry and it is null, JSON null should be written as value in StartEntry() return; } // Get the projected properties ProjectedPropertiesAnnotation projectedProperties = entry.GetAnnotation <ProjectedPropertiesAnnotation>(); // Write the properties this.jsonEntryAndFeedSerializer.AssertRecursionDepthIsZero(); this.jsonEntryAndFeedSerializer.WriteProperties( this.EntryEntityType, entry.Properties, false /* isComplexValue */, this.DuplicatePropertyNamesChecker, projectedProperties); this.jsonEntryAndFeedSerializer.AssertRecursionDepthIsZero(); // Close the object scope this.jsonOutputContext.JsonWriter.EndObjectScope(); }
/// <summary> /// Converts the object to ODataEntry or ODataEntityReferenceLink. /// </summary> /// <param name="value">The value of the <see cref="UriOperationParameter"/>.</param> /// <param name="elementType">The type of the value</param> /// <param name="useEntityReference">If true, use entity reference, instead of entity to serialize the parameter.</param> /// <returns>The converted result.</returns> private object ConvertToEntityValue(object value, Type elementType, bool useEntityReference) { object valueInODataFormat; if (!useEntityReference) { valueInODataFormat = this.propertyConverter.CreateODataEntry(elementType, value); ODataEntry entry = (ODataEntry)valueInODataFormat; SerializationTypeNameAnnotation serializedTypeNameAnnotation = entry.GetAnnotation <SerializationTypeNameAnnotation>(); if (serializedTypeNameAnnotation == null || string.IsNullOrEmpty(serializedTypeNameAnnotation.TypeName)) { throw Error.InvalidOperation(Strings.DataServiceException_GeneralError); } } else { EntityDescriptor resource = this.requestInfo.EntityTracker.GetEntityDescriptor(value); Uri link = resource.GetLatestIdentity(); valueInODataFormat = new ODataEntityReferenceLink() { Url = link, }; } return(valueInODataFormat); }
private MaterializerEntry(ODataEntry entry, DataServiceProtocolVersion maxProtocolVersion) { this.entry = entry; this.entityDescriptor = new System.Data.Services.Client.EntityDescriptor(maxProtocolVersion); SerializationTypeNameAnnotation annotation = entry.GetAnnotation <SerializationTypeNameAnnotation>(); this.entityDescriptor.ServerTypeName = (annotation != null) ? annotation.TypeName : (this.entityDescriptor.ServerTypeName = this.Entry.TypeName); }
private static void EndEntryXmlCustomizer(ODataEntry entry, XmlWriter entryWriter, XmlWriter parentWriter) { WritingEntityInfo annotation = entry.GetAnnotation <WritingEntityInfo>(); entryWriter.Close(); annotation.RequestInfo.FireWritingEntityEvent(annotation.Entity, annotation.EntryPayload.Root, null); annotation.EntryPayload.Root.WriteTo(parentWriter); }
protected override void EndEntry(ODataEntry entry) { if (entry != null) { ProjectedPropertiesAnnotation projectedProperties = entry.GetAnnotation <ProjectedPropertiesAnnotation>(); this.jsonEntryAndFeedSerializer.WriteProperties(base.EntryEntityType, entry.Properties, false, base.DuplicatePropertyNamesChecker, projectedProperties); this.jsonOutputContext.JsonWriter.EndObjectScope(); } }
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> /// Creates the materializer entry. /// </summary> /// <param name="entry">The entry.</param> /// <param name="format">The format the entry was read in.</param> /// <param name="isTracking">True if the contents of the entry will be tracked in the context, otherwise False.</param> /// <param name="model">The client model.</param> /// <returns>A new materializer entry.</returns> public static MaterializerEntry CreateEntry(ODataEntry entry, ODataFormat format, bool isTracking, ClientEdmModel model) { Debug.Assert(entry.GetAnnotation <MaterializerEntry>() == null, "MaterializerEntry has already been created."); MaterializerEntry materializerEntry = new MaterializerEntry(entry, format, isTracking, model); entry.SetAnnotation <MaterializerEntry>(materializerEntry); return(materializerEntry); }
public static AtomEntryMetadata Atom(this ODataEntry entry) { ExceptionUtils.CheckArgumentNotNull <ODataEntry>(entry, "entry"); AtomEntryMetadata annotation = entry.GetAnnotation <AtomEntryMetadata>(); if (annotation == null) { annotation = new AtomEntryMetadata(); entry.SetAnnotation <AtomEntryMetadata>(annotation); } return(annotation); }
private object CreateNestedEntityAndApplyProperties(ODataEntry entry, IEdmEntityTypeReference elementType, ODataDeserializerReadContext readContext) { ODataEntryAnnotation annotation = entry.GetAnnotation <ODataEntryAnnotation>(); Contract.Assert(annotation != null); CreateEntityResource(annotation, elementType, readContext); ODataEntryDeserializer deserializer = DeserializerProvider.GetODataDeserializer(elementType); return(deserializer.ReadInline(entry, readContext)); }
/// <summary> /// Determines the entity type name to write to the payload. /// </summary> /// <param name="expectedTypeName">The expected type name, e.g. the base type of the set or the nav prop.</param> /// <param name="entry">The ODataEntry whose type is to be written.</param> /// <returns>Type name to write to the payload, or null if no type name should be written.</returns> internal override string GetEntryTypeNameForWriting(string expectedTypeName, ODataEntry entry) { Debug.Assert(entry != null, "entry != null"); SerializationTypeNameAnnotation typeNameAnnotation = entry.GetAnnotation<SerializationTypeNameAnnotation>(); if (typeNameAnnotation != null) { return typeNameAnnotation.TypeName; } return entry.TypeName; }
/// <summary> /// Fires the WritingEntity event, and then copies the payload into the parent writer. /// </summary> /// <param name="entry">ODataEntry that is currently getting serialized.</param> /// <param name="entryWriter">XmlWriter writer instance that got returned by StartEntryXmlCustomizer method.</param> /// <param name="parentWriter">Parent writer to which the payload needs to get copied to, after firing the event.</param> private static void EndEntryXmlCustomizer(ODataEntry entry, XmlWriter entryWriter, XmlWriter parentWriter) { WritingEntityInfo writingEntityInfo = entry.GetAnnotation <WritingEntityInfo>(); Debug.Assert(writingEntityInfo.RequestInfo.HasWritingEventHandlers, "this.requestInfo.HasWritingEventHandlers"); #if PORTABLELIB entryWriter.Dispose(); #else entryWriter.Close(); #endif writingEntityInfo.RequestInfo.FireWritingEntityEvent(writingEntityInfo.Entity, (XElement)writingEntityInfo.EntryPayload.Root, null); writingEntityInfo.EntryPayload.Root.WriteTo(parentWriter); }
/// <summary> /// Determines the entity type name to write to the payload. /// </summary> /// <param name="expectedTypeName">The expected type name, e.g. the base type of the set or the nav prop.</param> /// <param name="entry">The ODataEntry whose type is to be written.</param> /// <returns>Type name to write to the payload, or null if no type name should be written.</returns> internal override string GetEntryTypeNameForWriting(string expectedTypeName, ODataEntry entry) { Debug.Assert(entry != null, "entry != null"); SerializationTypeNameAnnotation typeNameAnnotation = entry.GetAnnotation <SerializationTypeNameAnnotation>(); if (typeNameAnnotation != null) { return(typeNameAnnotation.TypeName); } return(entry.TypeName); }
protected override void StartEntry(ODataEntry entry) { if (entry == null) { this.jsonOutputContext.JsonWriter.WriteValue((string)null); } else { this.jsonOutputContext.JsonWriter.StartObjectScope(); ProjectedPropertiesAnnotation projectedProperties = entry.GetAnnotation <ProjectedPropertiesAnnotation>(); this.jsonEntryAndFeedSerializer.WriteEntryMetadata(entry, projectedProperties, base.EntryEntityType, base.DuplicatePropertyNamesChecker); } }
public static AtomEntryMetadata Atom(this ODataEntry entry) { ExceptionUtils.CheckArgumentNotNull(entry, "entry"); AtomEntryMetadata entryMetadata = entry.GetAnnotation <AtomEntryMetadata>(); if (entryMetadata == null) { entryMetadata = new AtomEntryMetadata(); entry.SetAnnotation(entryMetadata); } return(entryMetadata); }
private ResourceType GetEntryResourceType(ODataEntry entry, ResourceType expectedType) { Debug.Assert(entry != null, "entry != null"); Debug.Assert(expectedType != null, "We must always have an expected type for entities."); Debug.Assert(expectedType.ResourceTypeKind == ResourceTypeKind.EntityType, "Expected type for entities must be an entity type."); ResourceType resourceType; // Note that we can't rely on the ODataLib handling in this case completely. // In the WCF DS Server mode the ODataLib uses the lax validation, which means that it doesn't actually // validate almost anything (to follow the same logic as used for complex types). // So we have to implement the validation logic here on top of ODataLib. // We also can't use the type name reported by the entry property always, as that is the resolved type name. // It's safer to use the actual type name from the payload if it's available. string payloadTypeName = entry.TypeName; SerializationTypeNameAnnotation serializationTypeNameAnnotation = entry.GetAnnotation <SerializationTypeNameAnnotation>(); if (serializationTypeNameAnnotation != null) { payloadTypeName = serializationTypeNameAnnotation.TypeName; } // If the type is not specified in the payload, we assume the type to be the expected type. if (String.IsNullOrEmpty(payloadTypeName)) { // Check if the expected type takes part in inheritance. resourceType = expectedType; if (this.Service.Provider.HasDerivedTypes(resourceType)) { throw DataServiceException.CreateBadRequestError(Microsoft.OData.Service.Strings.BadRequest_TypeInformationMustBeSpecifiedForInhertiance); } } else { // Otherwise, try and resolve the name specified in the payload. resourceType = this.Service.Provider.TryResolveResourceType(payloadTypeName); if (resourceType == null) { throw DataServiceException.CreateBadRequestError(Microsoft.OData.Service.Strings.BadRequest_InvalidTypeName(payloadTypeName)); } if (!expectedType.IsAssignableFrom(resourceType)) { throw DataServiceException.CreateBadRequestError(Microsoft.OData.Service.Strings.BadRequest_InvalidTypeSpecified(payloadTypeName, expectedType.FullName)); } } return(resourceType); }
/// <summary> /// Visits an entry item. /// </summary> /// <param name="entry">The entry item to visit.</param> /// <returns>An ODataPayloadElement representing the entry.</returns> protected override ODataPayloadElement VisitEntry(ODataEntry entry) { ExceptionUtilities.CheckArgumentNotNull(entry, "entry"); EntityInstance entity = (EntityInstance)base.VisitEntry(entry); var atomMetadata = entry.GetAnnotation <AtomEntryMetadata>(); if (atomMetadata != null) { ConvertAtomEntryMetadata(atomMetadata, entity); } return(entity); }
/// <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); }
protected override void StartEntry(ODataEntry entry) { this.CheckAndWriteParentNavigationLinkStartForInlineElement(); if (entry != null) { this.StartEntryXmlCustomization(entry); this.atomOutputContext.XmlWriter.WriteStartElement("", "entry", "http://www.w3.org/2005/Atom"); if (base.IsTopLevel) { this.atomEntryAndFeedSerializer.WriteBaseUriAndDefaultNamespaceAttributes(); } string eTag = entry.ETag; if (eTag != null) { ODataAtomWriterUtils.WriteETag(this.atomOutputContext.XmlWriter, eTag); } AtomEntryScope currentEntryScope = this.CurrentEntryScope; AtomEntryMetadata entryMetadata = entry.Atom(); string id = entry.Id; if (id != null) { this.atomEntryAndFeedSerializer.WriteEntryId(id); currentEntryScope.SetWrittenElement(AtomElement.Id); } string typeName = entry.TypeName; SerializationTypeNameAnnotation annotation = entry.GetAnnotation <SerializationTypeNameAnnotation>(); if (annotation != null) { typeName = annotation.TypeName; } this.atomEntryAndFeedSerializer.WriteEntryTypeName(typeName, entryMetadata); Uri editLink = entry.EditLink; if (editLink != null) { this.atomEntryAndFeedSerializer.WriteEntryEditLink(editLink, entryMetadata); currentEntryScope.SetWrittenElement(AtomElement.EditLink); } Uri readLink = entry.ReadLink; if (readLink != null) { this.atomEntryAndFeedSerializer.WriteEntryReadLink(readLink, entryMetadata); currentEntryScope.SetWrittenElement(AtomElement.ReadLink); } } }
private void WriteEntry(ODataWriter writer, ODataEntry entry) { writer.WriteStart(entry); var annotation = entry.GetAnnotation <ODataEntryNavigationLinksObjectModelAnnotation>(); ODataNavigationLink navLink = null; if (annotation != null) { for (int i = 0; i < annotation.Count; ++i) { bool found = annotation.TryGetNavigationLinkAt(i, out navLink); ExceptionUtilities.Assert(found, "Navigation links should be ordered sequentially for writing"); this.WriteNavigationLink(writer, navLink); } } writer.WriteEnd(); }
/// <summary> /// Determines the entity type name to write to the payload. /// </summary> /// <param name="expectedTypeName">The expected type name, e.g. the base type of the set or the nav prop.</param> /// <param name="entry">The ODataEntry whose type is to be written.</param> /// <returns>Type name to write to the payload, or null if no type name should be written.</returns> internal override string GetEntryTypeNameForWriting(string expectedTypeName, ODataEntry entry) { Debug.Assert(entry != null, "entry != null"); SerializationTypeNameAnnotation typeNameAnnotation = entry.GetAnnotation<SerializationTypeNameAnnotation>(); if (typeNameAnnotation != null) { return typeNameAnnotation.TypeName; } // We only write entity type names in Json Light if it's more derived (different) from the expected type name. string entryTypeName = entry.TypeName; if (expectedTypeName != entryTypeName) { return entryTypeName; } return null; }
public override object ReadInline(object item, ODataDeserializerReadContext readContext) { ODataEntry topLevelEntry = item as ODataEntry; if (item == null) { throw Error.Argument("item", SRResources.ItemMustBeOfType, typeof(ODataEntry).Name); } ODataEntryAnnotation topLevelEntryAnnotation = topLevelEntry.GetAnnotation <ODataEntryAnnotation>(); Contract.Assert(topLevelEntryAnnotation != null); RecurseEnter(readContext); ApplyEntityProperties(topLevelEntry, topLevelEntryAnnotation, readContext); RecurseLeave(readContext); return(topLevelEntryAnnotation.EntityResource); }
/// <summary> /// Determines the entity type name to write to the payload. /// </summary> /// <param name="expectedTypeName">The expected type name, e.g. the base type of the set or the nav prop.</param> /// <param name="entry">The ODataEntry whose type is to be written.</param> /// <returns>Type name to write to the payload, or null if no type name should be written.</returns> internal override string GetEntryTypeNameForWriting(string expectedTypeName, ODataEntry entry) { Debug.Assert(entry != null, "entry != null"); SerializationTypeNameAnnotation typeNameAnnotation = entry.GetAnnotation <SerializationTypeNameAnnotation>(); if (typeNameAnnotation != null) { return(typeNameAnnotation.TypeName); } // We only write entity type names in Json Light if it's more derived (different) from the expected type name. string entryTypeName = entry.TypeName; if (expectedTypeName != entryTypeName) { return(entryTypeName); } return(null); }
/// <summary> /// Start writing an entry. /// </summary> /// <param name="entry">The entry to write.</param> protected override void StartEntry(ODataEntry entry) { if (entry == null) { Debug.Assert( this.ParentNavigationLink != null && !this.ParentNavigationLink.IsCollection.Value, "when entry == null, it has to be and expanded single entry navigation"); // this is a null expanded single entry and it is null, so write a JSON null as value. this.jsonOutputContext.JsonWriter.WriteValue(null); return; } // Write just the object start, nothing else, since we might not have complete information yet this.jsonOutputContext.JsonWriter.StartObjectScope(); // Get the projected properties ProjectedPropertiesAnnotation projectedProperties = entry.GetAnnotation <ProjectedPropertiesAnnotation>(); // Write the metadata this.jsonEntryAndFeedSerializer.WriteEntryMetadata(entry, projectedProperties, this.EntryEntityType, this.DuplicatePropertyNamesChecker); }
public void SetsAtomEntryMetadataAnnotation() { // Arrange var v2FeedPackage = new V2FeedPackage() { Id = "SomePackageId", Version = "1.0.0", Title = "Title", Authors = ".NET Foundation", LastUpdated = DateTime.UtcNow, Summary = "Summary" }; var annotationStrategy = new V2FeedPackageAnnotationStrategy(_contentType); var oDataEntry = new ODataEntry(); var request = CreateHttpRequestMessage("https://localhost/api/v2/Packages"); var expectedAtomEntryMetadataAnnotation = new AtomEntryMetadata() { Title = v2FeedPackage.Id, Authors = new[] { new AtomPersonMetadata { Name = v2FeedPackage.Authors } }, Updated = v2FeedPackage.LastUpdated, Summary = v2FeedPackage.Summary }; // Act annotationStrategy.Annotate(request, oDataEntry, v2FeedPackage); var actualAtomEntryMetadataAnnotation = oDataEntry.GetAnnotation <AtomEntryMetadata>(); // Assert Assert.Equal(expectedAtomEntryMetadataAnnotation.Title.Text, actualAtomEntryMetadataAnnotation.Title.Text); Assert.Equal(expectedAtomEntryMetadataAnnotation.Summary.Text, actualAtomEntryMetadataAnnotation.Summary.Text); Assert.Equal(expectedAtomEntryMetadataAnnotation.Authors.Single().Name, actualAtomEntryMetadataAnnotation.Authors.Single().Name); Assert.Equal(expectedAtomEntryMetadataAnnotation.Updated, actualAtomEntryMetadataAnnotation.Updated); }
public static MaterializerEntry GetEntry(ODataEntry entry) { return(entry.GetAnnotation <MaterializerEntry>()); }
/// <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> /// Start writing an entry. /// </summary> /// <param name="entry">The entry to write.</param> protected override void StartEntry(ODataEntry entry) { this.CheckAndWriteParentNavigationLinkStartForInlineElement(); Debug.Assert( this.ParentNavigationLink == null || !this.ParentNavigationLink.IsCollection.Value, "We should have already verified that the IsCollection matches the actual content of the link (feed/entry)."); if (entry == null) { Debug.Assert(this.ParentNavigationLink != null, "When entry == null, it has to be an expanded single entry navigation."); // this is a null expanded single entry and it is null, an empty <m:inline /> will be written. return; } this.StartEntryXmlCustomization(entry); // <entry> this.atomOutputContext.XmlWriter.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomEntryElementName, AtomConstants.AtomNamespace); if (this.IsTopLevel) { this.atomEntryAndFeedSerializer.WriteBaseUriAndDefaultNamespaceAttributes(); } string etag = entry.ETag; if (etag != null) { // TODO, ckerer: if this is a top-level entry also put the ETag into the headers. ODataAtomWriterUtils.WriteETag(this.atomOutputContext.XmlWriter, etag); } AtomEntryScope currentEntryScope = this.CurrentEntryScope; AtomEntryMetadata entryMetadata = entry.Atom(); // Write the id if it's available here. // If it's not available here we will try to write it at the end of the entry again. string entryId = entry.Id; if (entryId != null) { this.atomEntryAndFeedSerializer.WriteEntryId(entryId); currentEntryScope.SetWrittenElement(AtomElement.Id); } // <category term="type" scheme="odatascheme"/> // If no type information is provided, don't include the category element for type at all // NOTE: the validation of the type name is done by the core writer. string typeName = entry.TypeName; SerializationTypeNameAnnotation serializationTypeNameAnnotation = entry.GetAnnotation <SerializationTypeNameAnnotation>(); if (serializationTypeNameAnnotation != null) { typeName = serializationTypeNameAnnotation.TypeName; } this.atomEntryAndFeedSerializer.WriteEntryTypeName(typeName, entryMetadata); // Write the edit link if it's available here. // If it's not available here we will try to write it at the end of the entry again. Uri editLink = entry.EditLink; if (editLink != null) { this.atomEntryAndFeedSerializer.WriteEntryEditLink(editLink, entryMetadata); currentEntryScope.SetWrittenElement(AtomElement.EditLink); } // Write the self link if it's available here. // If it's not available here we will try to write it at the end of the entry again. Uri readLink = entry.ReadLink; if (readLink != null) { this.atomEntryAndFeedSerializer.WriteEntryReadLink(readLink, entryMetadata); currentEntryScope.SetWrittenElement(AtomElement.ReadLink); } }
private static XmlWriter StartEntryXmlCustomizer(ODataEntry entry, XmlWriter entryWriter) { return(entry.GetAnnotation <WritingEntityInfo>().EntryPayload.CreateWriter()); }
protected override void EndEntry(ODataEntry entry) { Debug.Assert( this.ParentNavigationLink == null || !this.ParentNavigationLink.IsCollection.Value, "We should have already verified that the IsCollection matches the actual content of the link (feed/entry)."); if (entry == null) { Debug.Assert(this.ParentNavigationLink != null, "When entry == null, it has to be an expanded single entry navigation."); // this is a null expanded single entry and it is null, an empty <m:inline /> will be written. this.CheckAndWriteParentNavigationLinkEndForInlineElement(); return; } IEdmEntityType entryType = this.EntryEntityType; // Initialize the property value cache and cache the entry properties. EntryPropertiesValueCache propertyValueCache = new EntryPropertiesValueCache(entry); // NOTE: when writing, we assume the model has been validated already and thus pass int.MaxValue for the maxMappingCount. ODataEntityPropertyMappingCache epmCache = this.atomOutputContext.Model.EnsureEpmCache(entryType, /*maxMappingCount*/ int.MaxValue); // Populate the property value cache based on the EPM source tree information. // We do this since we need to write custom EPM after the properties below and don't // want to cache all properties just for the case that they are used in a custom EPM. if (epmCache != null) { EpmWriterUtils.CacheEpmProperties(propertyValueCache, epmCache.EpmSourceTree); } // Get the projected properties annotation ProjectedPropertiesAnnotation projectedProperties = entry.GetAnnotation <ProjectedPropertiesAnnotation>(); AtomEntryScope currentEntryScope = this.CurrentEntryScope; AtomEntryMetadata entryMetadata = entry.Atom(); if (!currentEntryScope.IsElementWritten(AtomElement.Id)) { // NOTE: We write even null id, in that case we generate an empty atom:id element. this.atomEntryAndFeedSerializer.WriteEntryId(entry.Id); } Uri editLink = entry.EditLink; if (editLink != null && !currentEntryScope.IsElementWritten(AtomElement.EditLink)) { this.atomEntryAndFeedSerializer.WriteEntryEditLink(editLink, entryMetadata); } Uri readLink = entry.ReadLink; if (readLink != null && !currentEntryScope.IsElementWritten(AtomElement.ReadLink)) { this.atomEntryAndFeedSerializer.WriteEntryReadLink(readLink, entryMetadata); } // write entry metadata including syndication EPM AtomEntryMetadata epmEntryMetadata = null; if (epmCache != null) { ODataVersionChecker.CheckEntityPropertyMapping(this.atomOutputContext.Version, entryType, this.atomOutputContext.Model); epmEntryMetadata = EpmSyndicationWriter.WriteEntryEpm( epmCache.EpmTargetTree, propertyValueCache, entryType.ToTypeReference().AsEntity(), this.atomOutputContext); } this.atomEntryAndFeedSerializer.WriteEntryMetadata(entryMetadata, epmEntryMetadata, this.updatedTime); // stream properties IEnumerable <ODataProperty> streamProperties = propertyValueCache.EntryStreamProperties; if (streamProperties != null) { foreach (ODataProperty streamProperty in streamProperties) { this.atomEntryAndFeedSerializer.WriteStreamProperty( streamProperty, entryType, this.DuplicatePropertyNamesChecker, projectedProperties); } } // association links IEnumerable <ODataAssociationLink> associationLinks = entry.AssociationLinks; if (associationLinks != null) { foreach (ODataAssociationLink associationLink in associationLinks) { this.atomEntryAndFeedSerializer.WriteAssociationLink( associationLink, entryType, this.DuplicatePropertyNamesChecker, projectedProperties); } } // actions IEnumerable <ODataAction> actions = entry.Actions; if (actions != null) { foreach (ODataAction action in actions) { ValidationUtils.ValidateOperationNotNull(action, true); this.atomEntryAndFeedSerializer.WriteOperation(action); } } // functions IEnumerable <ODataFunction> functions = entry.Functions; if (functions != null) { foreach (ODataFunction function in functions) { ValidationUtils.ValidateOperationNotNull(function, false); this.atomEntryAndFeedSerializer.WriteOperation(function); } } // write the content this.WriteEntryContent( entry, entryType, propertyValueCache, epmCache == null ? null : epmCache.EpmSourceTree.Root, projectedProperties); // write custom EPM if (epmCache != null) { EpmCustomWriter.WriteEntryEpm( this.atomOutputContext.XmlWriter, epmCache.EpmTargetTree, propertyValueCache, entryType.ToTypeReference().AsEntity(), this.atomOutputContext); } // </entry> this.atomOutputContext.XmlWriter.WriteEndElement(); this.EndEntryXmlCustomization(entry); this.CheckAndWriteParentNavigationLinkEndForInlineElement(); }