public override void WriteStart(DataServiceODataWriterEntryArgs args) { CustomInstanceAnnotationsDescriptor current = new CustomInstanceAnnotationsDescriptor { TypeOfAnnotatedItem = typeof(ODataResource), Parent = writtenItemsStack.Count == 0 ? null : writtenItemsStack.Peek(), AnnotationsOnStart = new Collection <ODataInstanceAnnotation>(CustomInstanceAnnotationsGenerator.GetAnnotations("AnnotationOnEntry.AddedBeforeWriteStart.").ToList()), AnnotationsOnEnd = new Collection <ODataInstanceAnnotation>(CustomInstanceAnnotationsGenerator.GetAnnotations("AnnotationOnEntry.AddedAfterWriteStart.").Concat(CustomInstanceAnnotationsGenerator.GetAnnotationsWithTermInMetadata()).ToList()), }; AnnotatedItemsBaseline.Add(current); writtenItemsStack.Push(current); foreach (var annotation in current.AnnotationsOnStart) { args.Entry.InstanceAnnotations.Add(annotation); } base.WriteStart(args); foreach (var annotation in current.AnnotationsOnEnd) { args.Entry.InstanceAnnotations.Add(annotation); } }
public override void WriteStart(DataServiceODataWriterEntryArgs args) { base.WriteStart(args); if (args.Entry != null) { args.Entry.InstanceAnnotations.Add(new ODataInstanceAnnotation("MyNamespace.CustomAnnotation1", new ODataPrimitiveValue("Some annotation"))); } }
public override void WriteStart(DataServiceODataWriterEntryArgs args) { base.WriteStart(args); foreach (var annotation in CustomInstanceAnnotationsGenerator.GetDuplicateAnnotations()) { args.Entry.InstanceAnnotations.Add(annotation); } }
public override void WriteEnd(DataServiceODataWriterEntryArgs args) { if (WriteEntryEnd.Value != null) { if (WriteEntryEnd.Value(args)) { return; } } base.WriteEnd(args); }
/// <summary> /// The method called when an entry is about to be written. We will customize the entry, then let the runtime handle the rest. /// </summary> /// <param name="args"></param> public override void WriteStart(DataServiceODataWriterEntryArgs args) { // look for the special customized type (see below). var customized = args.Instance as CustomizedSimpleEntityType; if (customized != null) { // copy the customized values from the instance over to the entry. // At this point, you could generate these values programatically, change other values, or even hide properties! args.Entry.Id = customized.Identity; args.Entry.EditLink = customized.EditLink; } // call the base writer to actually write the start of the entry. base.WriteStart(args); }
private void WriteNavigationProperties(IExpandedResult expanded, EntityToSerialize entityToSerialize, bool resourceInstanceInFeed, IEnumerable <ProjectionNode> projectionNodesForCurrentResourceType) { Debug.Assert(entityToSerialize != null, "entityToSerialize != null"); Debug.Assert( projectionNodesForCurrentResourceType == null || projectionNodesForCurrentResourceType.All(projectionNode => projectionNode.TargetResourceType.IsAssignableFrom(entityToSerialize.ResourceType)), "The projection nodes must be filtered to the current resource type only."); IEnumerable <ResourceProperty> navProperties = projectionNodesForCurrentResourceType == null ? this.Provider.GetResourceSerializableProperties(this.CurrentContainer, entityToSerialize.ResourceType).Where(p => p.TypeKind == ResourceTypeKind.EntityType) : projectionNodesForCurrentResourceType.Where(p => p.Property != null && p.Property.TypeKind == ResourceTypeKind.EntityType).Select(p1 => p1.Property); foreach (ResourceProperty property in navProperties) { ResourcePropertyInfo navProperty = this.GetNavigationPropertyInfo(expanded, entityToSerialize.Entity, entityToSerialize.ResourceType, property); ODataNavigationLink navLink = this.GetNavigationLink(entityToSerialize, navProperty.Property); // WCF Data Services show performance degradation with JsonLight when entities have > 25 Navgations on writing entries // DEVNOTE: for performance reasons, if the link has no content due to the metadata level, and is not expanded // then don't tell ODataLib about it at all. if (navLink.Url == null && navLink.AssociationLinkUrl == null && !navProperty.Expand) { continue; } var linkArgs = new DataServiceODataWriterNavigationLinkArgs(navLink, this.Service.OperationContext); this.dataServicesODataWriter.WriteStart(linkArgs); if (navProperty.Expand) { object navPropertyValue = navProperty.Value; IExpandedResult navPropertyExpandedResult = navPropertyValue as IExpandedResult; object expandedPropertyValue = navPropertyExpandedResult != null? GetExpandedElement(navPropertyExpandedResult) : navPropertyValue; bool needPop = this.PushSegmentForProperty(navProperty.Property, entityToSerialize.ResourceType, navProperty.ExpandedNode); // if this.CurrentContainer is null, the target set of the navigation property is hidden. if (this.CurrentContainer != null) { if (navProperty.Property.Kind == ResourcePropertyKind.ResourceSetReference) { IEnumerable enumerable; bool collection = WebUtil.IsElementIEnumerable(expandedPropertyValue, out enumerable); Debug.Assert(collection, "metadata loading must have ensured that navigation set properties must implement IEnumerable"); QueryResultInfo queryResults = new QueryResultInfo(enumerable); try { queryResults.MoveNext(); Func <Uri> getNavPropertyRelativeUri = () => RequestUriProcessor.AppendUnescapedSegment(entityToSerialize.SerializedKey.RelativeEditLink, navLink.Name); Func <Uri> getNavPropertyAbsoluteUri = () => RequestUriProcessor.AppendUnescapedSegment(entityToSerialize.SerializedKey.AbsoluteEditLink, navLink.Name); this.WriteFeedElements(navPropertyExpandedResult, queryResults, navProperty.Property.ResourceType, navLink.Name, getNavPropertyRelativeUri, getNavPropertyAbsoluteUri, false); } finally { // After the navigation property is completely serialized, dispose the property value. WebUtil.Dispose(queryResults); } } else if (WebUtil.IsNullValue(expandedPropertyValue)) { // Write a null reference navigation property var entryArgs = new DataServiceODataWriterEntryArgs(null, null, this.Service.OperationContext); this.dataServicesODataWriter.WriteStart(entryArgs); this.dataServicesODataWriter.WriteEnd(entryArgs); } else { this.WriteEntry(navPropertyExpandedResult, expandedPropertyValue, resourceInstanceInFeed, navProperty.Property.ResourceType); } } this.PopSegmentName(needPop); } this.dataServicesODataWriter.WriteEnd(linkArgs); // end of navigation property } }
/// <summary>Write the entry element.</summary> /// <param name="expanded">Expanded result provider for the specified <paramref name="element"/>.</param> /// <param name="element">Element representing the entry element.</param> /// <param name="resourceInstanceInFeed">true if the resource instance being serialized is inside a feed; false otherwise.</param> /// <param name="expectedType">Expected type of the entry element.</param> private void WriteEntry(IExpandedResult expanded, object element, bool resourceInstanceInFeed, ResourceType expectedType) { Debug.Assert(element != null, "element != null"); Debug.Assert(expectedType != null && expectedType.ResourceTypeKind == ResourceTypeKind.EntityType, "expectedType != null && expectedType.ResourceTypeKind == ResourceTypeKind.EntityType"); this.IncrementSegmentResultCount(); ODataEntry entry = new ODataEntry(); if (!resourceInstanceInFeed) { entry.SetSerializationInfo(new ODataFeedAndEntrySerializationInfo { NavigationSourceName = this.CurrentContainer.Name, NavigationSourceEntityTypeName = this.CurrentContainer.ResourceType.FullName, ExpectedTypeName = expectedType.FullName }); } string title = expectedType.Name; #pragma warning disable 618 if (this.contentFormat == ODataFormat.Atom) #pragma warning restore 618 { AtomEntryMetadata entryAtom = new AtomEntryMetadata(); entryAtom.EditLink = new AtomLinkMetadata { Title = title }; entry.SetAnnotation(entryAtom); } ResourceType actualResourceType = WebUtil.GetNonPrimitiveResourceType(this.Provider, element); if (actualResourceType.ResourceTypeKind != ResourceTypeKind.EntityType) { // making sure that the actual resource type is an entity type throw new DataServiceException(500, Microsoft.OData.Service.Strings.BadProvider_InconsistentEntityOrComplexTypeUsage(actualResourceType.FullName)); } EntityToSerialize entityToSerialize = this.WrapEntity(element, actualResourceType); // populate the media resource, if the entity is a MLE. entry.MediaResource = this.GetMediaResource(entityToSerialize, title); // Write the type name this.PayloadMetadataPropertyManager.SetTypeName(entry, this.CurrentContainer.ResourceType.FullName, actualResourceType.FullName); // Write Id element this.PayloadMetadataPropertyManager.SetId(entry, () => entityToSerialize.SerializedKey.Identity); // Write "edit" link this.PayloadMetadataPropertyManager.SetEditLink(entry, () => entityToSerialize.SerializedKey.RelativeEditLink); // Write the etag property, if the type has etag properties this.PayloadMetadataPropertyManager.SetETag(entry, () => this.GetETagValue(element, actualResourceType)); IEnumerable <ProjectionNode> projectionNodes = this.GetProjections(); if (projectionNodes != null) { // Filter the projection nodes for the actual type of the entity // The projection node might refer to the property in a derived type. If the TargetResourceType of // the projection node is not a super type, then we do not want to serialize this property. projectionNodes = projectionNodes.Where(projectionNode => projectionNode.TargetResourceType.IsAssignableFrom(actualResourceType)); // Because we are going to enumerate through these multiple times, create a list. projectionNodes = projectionNodes.ToList(); // And add the annotation to tell ODataLib which properties to write into content (the projections) entry.SetAnnotation(new ProjectedPropertiesAnnotation(projectionNodes.Select(p => p.PropertyName))); } // Populate the advertised actions IEnumerable <ODataAction> actions; if (this.TryGetAdvertisedActions(entityToSerialize, resourceInstanceInFeed, out actions)) { foreach (ODataAction action in actions) { entry.AddAction(action); } } // Populate all the normal properties entry.Properties = this.GetEntityProperties(entityToSerialize, projectionNodes); // And start the entry var args = new DataServiceODataWriterEntryArgs(entry, element, this.Service.OperationContext); this.dataServicesODataWriter.WriteStart(args); // Now write all the navigation properties this.WriteNavigationProperties(expanded, entityToSerialize, resourceInstanceInFeed, projectionNodes); // And write the end of the entry this.dataServicesODataWriter.WriteEnd(args); #if ASTORIA_FF_CALLBACKS this.Service.InternalOnWriteItem(target, element); #endif }
private void WriteNavigationProperties(IExpandedResult expanded, EntityToSerialize entityToSerialize, bool resourceInstanceInFeed, IEnumerable<ProjectionNode> projectionNodesForCurrentResourceType) { Debug.Assert(entityToSerialize != null, "entityToSerialize != null"); Debug.Assert( projectionNodesForCurrentResourceType == null || projectionNodesForCurrentResourceType.All(projectionNode => projectionNode.TargetResourceType.IsAssignableFrom(entityToSerialize.ResourceType)), "The projection nodes must be filtered to the current resource type only."); IEnumerable<ResourceProperty> navProperties = projectionNodesForCurrentResourceType == null ? this.Provider.GetResourceSerializableProperties(this.CurrentContainer, entityToSerialize.ResourceType).Where(p => p.TypeKind == ResourceTypeKind.EntityType) : projectionNodesForCurrentResourceType.Where(p => p.Property != null && p.Property.TypeKind == ResourceTypeKind.EntityType).Select(p1 => p1.Property); foreach (ResourceProperty property in navProperties) { ResourcePropertyInfo navProperty = this.GetNavigationPropertyInfo(expanded, entityToSerialize.Entity, entityToSerialize.ResourceType, property); ODataNavigationLink navLink = this.GetNavigationLink(entityToSerialize, navProperty.Property); // WCF Data Services show performance degradation with JsonLight when entities have > 25 Navgations on writing entries // DEVNOTE: for performance reasons, if the link has no content due to the metadata level, and is not expanded // then don't tell ODataLib about it at all. if (navLink.Url == null && navLink.AssociationLinkUrl == null && !navProperty.Expand) { continue; } var linkArgs = new DataServiceODataWriterNavigationLinkArgs(navLink, this.Service.OperationContext); this.dataServicesODataWriter.WriteStart(linkArgs); if (navProperty.Expand) { object navPropertyValue = navProperty.Value; IExpandedResult navPropertyExpandedResult = navPropertyValue as IExpandedResult; object expandedPropertyValue = navPropertyExpandedResult != null ? GetExpandedElement(navPropertyExpandedResult) : navPropertyValue; bool needPop = this.PushSegmentForProperty(navProperty.Property, entityToSerialize.ResourceType, navProperty.ExpandedNode); // if this.CurrentContainer is null, the target set of the navigation property is hidden. if (this.CurrentContainer != null) { if (navProperty.Property.Kind == ResourcePropertyKind.ResourceSetReference) { IEnumerable enumerable; bool collection = WebUtil.IsElementIEnumerable(expandedPropertyValue, out enumerable); Debug.Assert(collection, "metadata loading must have ensured that navigation set properties must implement IEnumerable"); QueryResultInfo queryResults = new QueryResultInfo(enumerable); try { queryResults.MoveNext(); Func<Uri> getNavPropertyRelativeUri = () => RequestUriProcessor.AppendUnescapedSegment(entityToSerialize.SerializedKey.RelativeEditLink, navLink.Name); Func<Uri> getNavPropertyAbsoluteUri = () => RequestUriProcessor.AppendUnescapedSegment(entityToSerialize.SerializedKey.AbsoluteEditLink, navLink.Name); this.WriteFeedElements(navPropertyExpandedResult, queryResults, navProperty.Property.ResourceType, navLink.Name, getNavPropertyRelativeUri, getNavPropertyAbsoluteUri, false); } finally { // After the navigation property is completely serialized, dispose the property value. WebUtil.Dispose(queryResults); } } else if (WebUtil.IsNullValue(expandedPropertyValue)) { // Write a null reference navigation property var entryArgs = new DataServiceODataWriterEntryArgs(null, null, this.Service.OperationContext); this.dataServicesODataWriter.WriteStart(entryArgs); this.dataServicesODataWriter.WriteEnd(entryArgs); } else { this.WriteEntry(navPropertyExpandedResult, expandedPropertyValue, resourceInstanceInFeed, navProperty.Property.ResourceType); } } this.PopSegmentName(needPop); } this.dataServicesODataWriter.WriteEnd(linkArgs); // end of navigation property } }
/// <summary>Write the entry element.</summary> /// <param name="expanded">Expanded result provider for the specified <paramref name="element"/>.</param> /// <param name="element">Element representing the entry element.</param> /// <param name="resourceInstanceInFeed">true if the resource instance being serialized is inside a feed; false otherwise.</param> /// <param name="expectedType">Expected type of the entry element.</param> private void WriteEntry(IExpandedResult expanded, object element, bool resourceInstanceInFeed, ResourceType expectedType) { Debug.Assert(element != null, "element != null"); Debug.Assert(expectedType != null && expectedType.ResourceTypeKind == ResourceTypeKind.EntityType, "expectedType != null && expectedType.ResourceTypeKind == ResourceTypeKind.EntityType"); this.IncrementSegmentResultCount(); ODataEntry entry = new ODataEntry(); if (!resourceInstanceInFeed) { entry.SetSerializationInfo(new ODataFeedAndEntrySerializationInfo { NavigationSourceName = this.CurrentContainer.Name, NavigationSourceEntityTypeName = this.CurrentContainer.ResourceType.FullName, ExpectedTypeName = expectedType.FullName }); } string title = expectedType.Name; #pragma warning disable 618 if (this.contentFormat == ODataFormat.Atom) #pragma warning restore 618 { AtomEntryMetadata entryAtom = new AtomEntryMetadata(); entryAtom.EditLink = new AtomLinkMetadata { Title = title }; entry.SetAnnotation(entryAtom); } ResourceType actualResourceType = WebUtil.GetNonPrimitiveResourceType(this.Provider, element); if (actualResourceType.ResourceTypeKind != ResourceTypeKind.EntityType) { // making sure that the actual resource type is an entity type throw new DataServiceException(500, Microsoft.OData.Service.Strings.BadProvider_InconsistentEntityOrComplexTypeUsage(actualResourceType.FullName)); } EntityToSerialize entityToSerialize = this.WrapEntity(element, actualResourceType); // populate the media resource, if the entity is a MLE. entry.MediaResource = this.GetMediaResource(entityToSerialize, title); // Write the type name this.PayloadMetadataPropertyManager.SetTypeName(entry, this.CurrentContainer.ResourceType.FullName, actualResourceType.FullName); // Write Id element this.PayloadMetadataPropertyManager.SetId(entry, () => entityToSerialize.SerializedKey.Identity); // Write "edit" link this.PayloadMetadataPropertyManager.SetEditLink(entry, () => entityToSerialize.SerializedKey.RelativeEditLink); // Write the etag property, if the type has etag properties this.PayloadMetadataPropertyManager.SetETag(entry, () => this.GetETagValue(element, actualResourceType)); IEnumerable<ProjectionNode> projectionNodes = this.GetProjections(); if (projectionNodes != null) { // Filter the projection nodes for the actual type of the entity // The projection node might refer to the property in a derived type. If the TargetResourceType of // the projection node is not a super type, then we do not want to serialize this property. projectionNodes = projectionNodes.Where(projectionNode => projectionNode.TargetResourceType.IsAssignableFrom(actualResourceType)); // Because we are going to enumerate through these multiple times, create a list. projectionNodes = projectionNodes.ToList(); // And add the annotation to tell ODataLib which properties to write into content (the projections) entry.SetAnnotation(new ProjectedPropertiesAnnotation(projectionNodes.Select(p => p.PropertyName))); } // Populate the advertised actions IEnumerable<ODataAction> actions; if (this.TryGetAdvertisedActions(entityToSerialize, resourceInstanceInFeed, out actions)) { foreach (ODataAction action in actions) { entry.AddAction(action); } } // Populate all the normal properties entry.Properties = this.GetEntityProperties(entityToSerialize, projectionNodes); // And start the entry var args = new DataServiceODataWriterEntryArgs(entry, element, this.Service.OperationContext); this.dataServicesODataWriter.WriteStart(args); // Now write all the navigation properties this.WriteNavigationProperties(expanded, entityToSerialize, resourceInstanceInFeed, projectionNodes); // And write the end of the entry this.dataServicesODataWriter.WriteEnd(args); #if ASTORIA_FF_CALLBACKS this.Service.InternalOnWriteItem(target, element); #endif }