From c8650c99aeb5152ff5cd802d058601d284f1b169 Mon Sep 17 00:00:00 2001 From: ElizabethOkerio Date: Thu, 11 Aug 2022 11:48:14 +0300 Subject: [PATCH] bulk ops serialization changes --- .../DefaultODataSerializerProvider.cs | 4 + .../Serialization/ODataDeltaFeedSerializer.cs | 142 +++++++-- .../Serialization/ODataResourceSerializer.cs | 275 ++++++++++++------ .../Serialization/ODataSerializerContext.cs | 46 ++- .../Serialization/ODataSerializerHelper.cs | 113 +++++++ .../Microsoft.AspNet.OData.Shared.projitems | 1 + 6 files changed, 456 insertions(+), 125 deletions(-) create mode 100644 src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/ODataSerializerHelper.cs diff --git a/src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/DefaultODataSerializerProvider.cs b/src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/DefaultODataSerializerProvider.cs index fcefb7c6fd..7f28816ec2 100644 --- a/src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/DefaultODataSerializerProvider.cs +++ b/src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/DefaultODataSerializerProvider.cs @@ -112,6 +112,10 @@ internal ODataSerializer GetODataPayloadSerializerImpl(Type type, Func(); } + else if (TypeHelper.IsTypeAssignableFrom(typeof(IDeltaSet), type)) + { + return _rootContainer.GetRequiredService(); + } // Get the model. Using a Func to delay evaluation of the model // until after the above checks have passed. diff --git a/src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/ODataDeltaFeedSerializer.cs b/src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/ODataDeltaFeedSerializer.cs index d041abeb41..9424534bec 100644 --- a/src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/ODataDeltaFeedSerializer.cs +++ b/src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/ODataDeltaFeedSerializer.cs @@ -6,8 +6,11 @@ //------------------------------------------------------------------------------ using System; +using System.CodeDom; using System.Collections; +using System.Collections.Generic; using System.Diagnostics.Contracts; +using System.Reflection; using System.Runtime.Serialization; using System.Threading.Tasks; using Microsoft.AspNet.OData.Builder; @@ -25,6 +28,7 @@ namespace Microsoft.AspNet.OData.Formatter.Serialization public class ODataDeltaFeedSerializer : ODataEdmTypeSerializer { private const string DeltaFeed = "deltafeed"; + IEdmStructuredTypeReference _elementType; /// /// Initializes a new instance of . @@ -60,6 +64,7 @@ public override void WriteObject(object graph, Type type, ODataMessageWriter mes } IEdmTypeReference feedType = writeContext.GetEdmType(graph, type); + Contract.Assert(feedType != null); IEdmEntityTypeReference entityType = GetResourceType(feedType).AsEntity(); @@ -93,6 +98,7 @@ public override async Task WriteObjectAsync(object graph, Type type, ODataMessag } IEdmTypeReference feedType = writeContext.GetEdmType(graph, type); + Contract.Assert(feedType != null); IEdmEntityTypeReference entityType = GetResourceType(feedType).AsEntity(); @@ -186,6 +192,7 @@ private void WriteFeed(IEnumerable enumerable, IEdmTypeReference feedType, OData Contract.Assert(feedType != null); IEdmStructuredTypeReference elementType = GetResourceType(feedType); + _elementType = elementType; if (elementType.IsComplex()) { @@ -234,13 +241,31 @@ private void WriteFeed(IEnumerable enumerable, IEdmTypeReference feedType, OData } lastResource = entry; - IEdmChangedObject edmChangedObject = entry as IEdmChangedObject; - if (edmChangedObject == null) + + EdmDeltaEntityKind deltaEntityKind; + if (writeContext.IsUntyped) + { + IEdmChangedObject edmChangedObject = entry as IEdmChangedObject; + if (edmChangedObject == null) + { + throw new SerializationException(Error.Format(SRResources.CannotWriteType, GetType().Name, enumerable.GetType().FullName)); + } + + deltaEntityKind = edmChangedObject.DeltaKind; + } + else { - throw new SerializationException(Error.Format(SRResources.CannotWriteType, GetType().Name, enumerable.GetType().FullName)); + IDeltaSetItem deltaSetItem = entry as IDeltaSetItem; + + if (deltaSetItem == null) + { + throw new SerializationException(Error.Format(SRResources.CannotWriteType, GetType().Name, enumerable.GetType().FullName)); + } + + deltaEntityKind = deltaSetItem.DeltaKind; } - switch (edmChangedObject.DeltaKind) + switch (deltaEntityKind) { case EdmDeltaEntityKind.DeletedEntry: WriteDeltaDeletedEntry(entry, writer, writeContext); @@ -254,6 +279,7 @@ private void WriteFeed(IEnumerable enumerable, IEdmTypeReference feedType, OData case EdmDeltaEntityKind.Entry: { ODataResourceSerializer entrySerializer = SerializerProvider.GetEdmTypeSerializer(elementType) as ODataResourceSerializer; + if (entrySerializer == null) { throw new SerializationException( @@ -289,6 +315,7 @@ private async Task WriteFeedAsync(IEnumerable enumerable, IEdmTypeReference feed Contract.Assert(feedType != null); IEdmStructuredTypeReference elementType = GetResourceType(feedType); + _elementType = elementType; if (elementType.IsComplex()) { @@ -337,13 +364,32 @@ private async Task WriteFeedAsync(IEnumerable enumerable, IEdmTypeReference feed } lastResource = entry; - IEdmChangedObject edmChangedObject = entry as IEdmChangedObject; - if (edmChangedObject == null) + + EdmDeltaEntityKind deltaEntityKind; + if (writeContext.IsUntyped) { - throw new SerializationException(Error.Format(SRResources.CannotWriteType, GetType().Name, enumerable.GetType().FullName)); + IEdmChangedObject edmChangedObject = entry as IEdmChangedObject; + if (edmChangedObject == null) + { + throw new SerializationException(Error.Format(SRResources.CannotWriteType, GetType().Name, enumerable.GetType().FullName)); + } + + deltaEntityKind = edmChangedObject.DeltaKind; + } + else + { + IDeltaSetItem deltaSetItem = entry as IDeltaSetItem; + + if (deltaSetItem == null) + { + throw new SerializationException(Error.Format(SRResources.CannotWriteType, GetType().Name, enumerable.GetType().FullName)); + } + + deltaEntityKind = deltaSetItem.DeltaKind; } - switch (edmChangedObject.DeltaKind) + + switch (deltaEntityKind) { case EdmDeltaEntityKind.DeletedEntry: await WriteDeltaDeletedEntryAsync(entry, writer, writeContext); @@ -438,15 +484,23 @@ public virtual ODataDeltaResourceSet CreateODataDeltaFeed(IEnumerable feedInstan /// /// The object to be written. /// The to be used for writing. - /// The . + /// The . public virtual void WriteDeltaDeletedEntry(object graph, ODataWriter writer, ODataSerializerContext writeContext) { - ODataDeletedResource deletedResource = GetDeletedResource(graph); + ODataResourceSerializer serializer = SerializerProvider.GetEdmTypeSerializer(_elementType) as ODataResourceSerializer; + ResourceContext resourceContext = serializer.GetResourceContext(graph, writeContext); + SelectExpandNode selectExpandNode = serializer.CreateSelectExpandNode(resourceContext); - if (deletedResource != null) + if (selectExpandNode != null) { - writer.WriteStart(deletedResource); - writer.WriteEnd(); + ODataDeletedResource deletedResource = GetDeletedResource(graph, resourceContext, serializer, selectExpandNode, writeContext.IsUntyped); + + if (deletedResource != null) + { + writer.WriteStart(deletedResource); + serializer.WriteDeltaComplexProperties(selectExpandNode, resourceContext, writer); + writer.WriteEnd(); + } } } @@ -456,14 +510,22 @@ public virtual void WriteDeltaDeletedEntry(object graph, ODataWriter writer, ODa /// /// The object to be written. /// The to be used for writing. - /// The . + /// The . public virtual async Task WriteDeltaDeletedEntryAsync(object graph, ODataWriter writer, ODataSerializerContext writeContext) { - ODataDeletedResource deletedResource = GetDeletedResource(graph); - if (deletedResource != null) + ODataResourceSerializer serializer = SerializerProvider.GetEdmTypeSerializer(_elementType) as ODataResourceSerializer; + ResourceContext resourceContext = serializer.GetResourceContext(graph, writeContext); + SelectExpandNode selectExpandNode = serializer.CreateSelectExpandNode(resourceContext); + + if (selectExpandNode != null) { - await writer.WriteStartAsync(deletedResource); - await writer.WriteEndAsync(); + ODataDeletedResource deletedResource = GetDeletedResource(graph, resourceContext, serializer, selectExpandNode, writeContext.IsUntyped); + + if (deletedResource != null) + { + await writer.WriteStartAsync(deletedResource); + await writer.WriteEndAsync(); + } } } @@ -473,7 +535,7 @@ public virtual async Task WriteDeltaDeletedEntryAsync(object graph, ODataWriter /// /// The object to be written. /// The to be used for writing. - /// The . + /// The . public virtual void WriteDeltaDeletedLink(object graph, ODataWriter writer, ODataSerializerContext writeContext) { ODataDeltaDeletedLink deltaDeletedLink = GetDeletedLink(graph); @@ -489,7 +551,7 @@ public virtual void WriteDeltaDeletedLink(object graph, ODataWriter writer, ODat /// /// The object to be written. /// The to be used for writing. - /// The . + /// The . public virtual async Task WriteDeltaDeletedLinkAsync(object graph, ODataWriter writer, ODataSerializerContext writeContext) { ODataDeltaDeletedLink deltaDeletedLink = GetDeletedLink(graph); @@ -505,7 +567,7 @@ public virtual async Task WriteDeltaDeletedLinkAsync(object graph, ODataWriter w /// /// The object to be written. /// The to be used for writing. - /// The . + /// The . public virtual void WriteDeltaLink(object graph, ODataWriter writer, ODataSerializerContext writeContext) { ODataDeltaLink deltaLink = GetDeltaLink(graph); @@ -521,7 +583,7 @@ public virtual void WriteDeltaLink(object graph, ODataWriter writer, ODataSerial /// /// The object to be written. /// The to be used for writing. - /// The . + /// The . public async Task WriteDeltaLinkAsync(object graph, ODataWriter writer, ODataSerializerContext writeContext) { ODataDeltaLink deltaLink = GetDeltaLink(graph); @@ -531,22 +593,42 @@ public async Task WriteDeltaLinkAsync(object graph, ODataWriter writer, ODataSer } } - private ODataDeletedResource GetDeletedResource(object graph) + + private ODataDeletedResource GetDeletedResource(object graph, ResourceContext resourceContext, ODataResourceSerializer serializer, SelectExpandNode selectExpandNode, bool isUntyped) { - EdmDeltaDeletedEntityObject edmDeltaDeletedEntity = graph as EdmDeltaDeletedEntityObject; - if (edmDeltaDeletedEntity == null) + string navigationSource; + ODataDeletedResource deletedResource = serializer.CreateDeletedResource(selectExpandNode, resourceContext); + + if (isUntyped) { - throw new SerializationException(Error.Format(SRResources.CannotWriteType, GetType().Name, graph.GetType().FullName)); + EdmDeltaDeletedEntityObject edmDeltaDeletedEntity = graph as EdmDeltaDeletedEntityObject; + if (edmDeltaDeletedEntity == null) + { + throw new SerializationException(Error.Format(SRResources.CannotWriteType, GetType().Name, graph.GetType().FullName)); + } + + deletedResource.Id = StringToUri(edmDeltaDeletedEntity.Id ?? string.Empty); + deletedResource.Reason = edmDeltaDeletedEntity.Reason; + navigationSource = edmDeltaDeletedEntity.NavigationSource.Name; } + else + { + IDeltaDeletedEntityObject deltaDeletedEntity = graph as IDeltaDeletedEntityObject; + if (deltaDeletedEntity == null) + { + throw new SerializationException(Error.Format(SRResources.CannotWriteType, GetType().Name, graph.GetType().FullName)); + } - Uri id = StringToUri(edmDeltaDeletedEntity.Id); - ODataDeletedResource deletedResource = new ODataDeletedResource(id, edmDeltaDeletedEntity.Reason); + deletedResource.Id = deltaDeletedEntity.Id; + deletedResource.Reason = deltaDeletedEntity.Reason; + navigationSource = deltaDeletedEntity.NavigationSource; + } - if (edmDeltaDeletedEntity.NavigationSource != null) + if (navigationSource != null) { ODataResourceSerializationInfo serializationInfo = new ODataResourceSerializationInfo { - NavigationSourceName = edmDeltaDeletedEntity.NavigationSource.Name + NavigationSourceName = navigationSource }; deletedResource.SetSerializationInfo(serializationInfo); } diff --git a/src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/ODataResourceSerializer.cs b/src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/ODataResourceSerializer.cs index 8d4c2c5a92..86f6fe2413 100644 --- a/src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/ODataResourceSerializer.cs +++ b/src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/ODataResourceSerializer.cs @@ -184,7 +184,7 @@ public virtual Task WriteDeltaObjectInlineAsync(object graph, IEdmTypeReference { throw new SerializationException(Error.Format(SRResources.CannotSerializerNull, Resource)); } - + return WriteDeltaResourceAsync(graph, writer, writeContext); } @@ -202,12 +202,13 @@ private void WriteDeltaResource(object graph, ODataWriter writer, ODataSerialize { writer.WriteStart(resource); WriteDeltaComplexProperties(selectExpandNode, resourceContext, writer); + WriteDeltaNavigationProperties(selectExpandNode, resourceContext, writer); //TODO: Need to add support to write Navigation Links, etc. using Delta Writer //https://github.com/OData/odata.net/issues/155 //CLEANUP: merge delta logic with regular logic; requires common base between ODataWriter and ODataDeltaWriter //WriteDynamicComplexProperties(resourceContext, writer); //WriteNavigationLinks(selectExpandNode.SelectedNavigationProperties, resourceContext, writer); - //WriteExpandedNavigationProperties(selectExpandNode.ExpandedNavigationProperties, resourceContext, writer); + //WriteExpandedNavigationProperties(selectExpandNode, resourceContext, writer); writer.WriteEnd(); } @@ -226,6 +227,7 @@ private async Task WriteDeltaResourceAsync(object graph, ODataWriter writer, ODa { await writer.WriteStartAsync(resource); await WriteDeltaComplexPropertiesAsync(selectExpandNode, resourceContext, writer); + await WriteDeltaNavigationPropertiesAsync(selectExpandNode, resourceContext, writer); //TODO: Need to add support to write Navigation Links, etc. using Delta Writer //https://github.com/OData/odata.net/issues/155 //CLEANUP: merge delta logic with regular logic; requires common base between ODataWriter and ODataDeltaWriter @@ -238,7 +240,7 @@ private async Task WriteDeltaResourceAsync(object graph, ODataWriter writer, ODa } } - private ResourceContext GetResourceContext(object graph, ODataSerializerContext writeContext) + internal ResourceContext GetResourceContext(object graph, ODataSerializerContext writeContext) { Contract.Assert(writeContext != null); @@ -253,7 +255,7 @@ private ResourceContext GetResourceContext(object graph, ODataSerializerContext return resourceContext; } - private void WriteDeltaComplexProperties(SelectExpandNode selectExpandNode, + internal void WriteDeltaComplexProperties(SelectExpandNode selectExpandNode, ResourceContext resourceContext, ODataWriter writer) { Contract.Assert(resourceContext != null); @@ -275,6 +277,48 @@ private void WriteDeltaComplexProperties(SelectExpandNode selectExpandNode, } } + internal void WriteDeltaNavigationProperties(SelectExpandNode selectExpandNode, ResourceContext resourceContext, ODataWriter writer) + { + Contract.Assert(resourceContext != null); + Contract.Assert(writer != null); + + IEnumerable> navigationProperties = GetNavigationPropertiesToWrite(selectExpandNode, resourceContext); + + foreach (KeyValuePair navigationProperty in navigationProperties) + { + ODataNestedResourceInfo nestedResourceInfo = new ODataNestedResourceInfo + { + IsCollection = navigationProperty.Key.Type.IsCollection(), + Name = navigationProperty.Key.Name + }; + + writer.WriteStart(nestedResourceInfo); + WriteDeltaComplexAndExpandedNavigationProperty(navigationProperty.Key, null, resourceContext, writer, navigationProperty.Value); + writer.WriteEnd(); + } + } + + internal async Task WriteDeltaNavigationPropertiesAsync(SelectExpandNode selectExpandNode, ResourceContext resourceContext, ODataWriter writer) + { + Contract.Assert(resourceContext != null); + Contract.Assert(writer != null); + + IEnumerable> navigationProperties = GetNavigationPropertiesToWrite(selectExpandNode, resourceContext); + + foreach (KeyValuePair navigationProperty in navigationProperties) + { + ODataNestedResourceInfo nestedResourceInfo = new ODataNestedResourceInfo + { + IsCollection = navigationProperty.Key.Type.IsCollection(), + Name = navigationProperty.Key.Name + }; + + await writer.WriteStartAsync(nestedResourceInfo); + await WriteDeltaComplexAndExpandedNavigationPropertyAsync(navigationProperty.Key, null, resourceContext, writer, navigationProperty.Value); + await writer.WriteEndAsync(); + } + } + private async Task WriteDeltaComplexPropertiesAsync(SelectExpandNode selectExpandNode, ResourceContext resourceContext, ODataWriter writer) { @@ -298,7 +342,7 @@ private async Task WriteDeltaComplexPropertiesAsync(SelectExpandNode selectExpan } private void WriteDeltaComplexAndExpandedNavigationProperty(IEdmProperty edmProperty, SelectExpandClause selectExpandClause, - ResourceContext resourceContext, ODataWriter writer) + ResourceContext resourceContext, ODataWriter writer, Type type = null) { Contract.Assert(edmProperty != null); Contract.Assert(resourceContext != null); @@ -331,6 +375,7 @@ private void WriteDeltaComplexAndExpandedNavigationProperty(IEdmProperty edmProp { // create the serializer context for the complex and expanded item. ODataSerializerContext nestedWriteContext = new ODataSerializerContext(resourceContext, selectExpandClause, edmProperty); + nestedWriteContext.Type = type; // write object. @@ -355,7 +400,7 @@ private void WriteDeltaComplexAndExpandedNavigationProperty(IEdmProperty edmProp } private async Task WriteDeltaComplexAndExpandedNavigationPropertyAsync(IEdmProperty edmProperty, SelectExpandClause selectExpandClause, - ResourceContext resourceContext, ODataWriter writer) + ResourceContext resourceContext, ODataWriter writer, Type type = null) { Contract.Assert(edmProperty != null); Contract.Assert(resourceContext != null); @@ -388,6 +433,7 @@ await writer.WriteStartAsync(new ODataResourceSet { // create the serializer context for the complex and expanded item. ODataSerializerContext nestedWriteContext = new ODataSerializerContext(resourceContext, selectExpandClause, edmProperty); + nestedWriteContext.Type = type; // write object. @@ -434,7 +480,7 @@ private static IEnumerable CreateODataPropertiesFromDynamicType(E .FirstOrDefault(p => p.Name.Equals(prop.Key)); if (prop.Value != null && (prop.Value is DynamicTypeWrapper || (prop.Value is IEnumerable))) - { + { if (edmProperty != null) { dynamicTypeProperties.Add(edmProperty, prop.Value); @@ -451,7 +497,7 @@ private static IEnumerable CreateODataPropertiesFromDynamicType(E Value = new ODataNullValue() }; } - else + else { if (edmProperty != null) { @@ -688,6 +734,14 @@ public virtual SelectExpandNode CreateSelectExpandNode(ResourceContext resourceC /// The created . public virtual ODataResource CreateResource(SelectExpandNode selectExpandNode, ResourceContext resourceContext) { + ODataResource resource = CreateResourceBase(selectExpandNode, resourceContext, false) as ODataResource; + return resource; + } + + + private ODataResourceBase CreateResourceBase(SelectExpandNode selectExpandNode, ResourceContext resourceContext, bool isDeletedResource) + { + if (selectExpandNode == null) { throw Error.ArgumentNull("selectExpandNode"); @@ -700,6 +754,14 @@ public virtual ODataResource CreateResource(SelectExpandNode selectExpandNode, R if (resourceContext.SerializerContext.ExpandReference) { + if (isDeletedResource) + { + return new ODataDeletedResource + { + Id = resourceContext.GenerateSelfLink(false) + }; + } + return new ODataResource { Id = resourceContext.GenerateSelfLink(false) @@ -707,11 +769,25 @@ public virtual ODataResource CreateResource(SelectExpandNode selectExpandNode, R } string typeName = resourceContext.StructuredType.FullTypeName(); - ODataResource resource = new ODataResource + ODataResourceBase resource; + + if (isDeletedResource) { - TypeName = typeName, - Properties = CreateStructuralPropertyBag(selectExpandNode, resourceContext), - }; + resource = new ODataDeletedResource + { + TypeName = typeName, + Properties = CreateStructuralPropertyBag(selectExpandNode, resourceContext), + }; + } + else + { + resource = new ODataResource + { + TypeName = typeName, + Properties = CreateStructuralPropertyBag(selectExpandNode, resourceContext), + }; + } + if (resourceContext.EdmObject is EdmDeltaEntityObject && resourceContext.NavigationSource != null) { @@ -760,7 +836,7 @@ public virtual ODataResource CreateResource(SelectExpandNode selectExpandNode, R AddTypeNameAnnotationAsNeeded(resource, pathType, resourceContext.SerializerContext.MetadataLevel); } - if (resourceContext.StructuredType.TypeKind == EdmTypeKind.Entity && resourceContext.NavigationSource != null) + if (!isDeletedResource && resourceContext.StructuredType.TypeKind == EdmTypeKind.Entity && resourceContext.NavigationSource != null) { if (!(resourceContext.NavigationSource is IEdmContainedEntitySet)) { @@ -794,6 +870,19 @@ public virtual ODataResource CreateResource(SelectExpandNode selectExpandNode, R return resource; } + /// + /// Creates the to be written while writing this resource. + /// + /// The describing the response graph. + /// The context for the resource instance being written. + /// The created . + public virtual ODataDeletedResource CreateDeletedResource(SelectExpandNode selectExpandNode, ResourceContext resourceContext) + { + ODataDeletedResource resource = CreateResourceBase(selectExpandNode, resourceContext, true) as ODataDeletedResource; + return resource; + } + + /// /// Appends the dynamic properties of primitive, enum or the collection of them into the given . /// If the dynamic property is a property of the complex or collection of complex, it will be saved into @@ -804,12 +893,12 @@ public virtual ODataResource CreateResource(SelectExpandNode selectExpandNode, R /// The context for the resource instance being written. [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "Relies on many classes.")] [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "These are simple conversion function and cannot be split up.")] - public virtual void AppendDynamicProperties(ODataResource resource, SelectExpandNode selectExpandNode, + public virtual void AppendDynamicProperties(ODataResourceBase resource, SelectExpandNode selectExpandNode, ResourceContext resourceContext) { Contract.Assert(resource != null); Contract.Assert(selectExpandNode != null); - Contract.Assert(resourceContext != null); + Contract.Assert(resourceContext != null); if (!resourceContext.StructuredType.IsOpen || // non-open type (!selectExpandNode.SelectAllDynamicProperties && selectExpandNode.SelectedDynamicProperties == null)) @@ -931,102 +1020,60 @@ public virtual void AppendDynamicProperties(ODataResource resource, SelectExpand /// /// The describing the resource, which is being annotated. /// The context for the resource instance, which is being annotated. - public virtual void AppendInstanceAnnotations(ODataResource resource, ResourceContext resourceContext) + public virtual void AppendInstanceAnnotations(ODataResourceBase resource, ResourceContext resourceContext) { IEdmStructuredType structuredType = resourceContext.StructuredType; IEdmStructuredObject structuredObject = resourceContext.EdmObject; + + //For appending transient and persistent instance annotations for both enity object and normal resources + PropertyInfo instanceAnnotationInfo = EdmLibHelpers.GetInstanceAnnotationsContainer(structuredType, resourceContext.EdmModel); - object value; - - if (instanceAnnotationInfo == null || structuredObject == null || - !structuredObject.TryGetPropertyValue(instanceAnnotationInfo.Name, out value) || value == null) - { - return; - } + EdmEntityObject edmEntityObject = null; + object instanceAnnotations = null; + IODataInstanceAnnotationContainer transientAnnotations = null; - IODataInstanceAnnotationContainer instanceAnnotationContainer = value as IODataInstanceAnnotationContainer; + IDelta delta = null; - if (instanceAnnotationContainer != null) + if (resourceContext.SerializerContext.IsDeltaOfT) { - IDictionary clrAnnotations = instanceAnnotationContainer.GetResourceAnnotations(); - - if (clrAnnotations != null) - { - foreach (KeyValuePair annotation in clrAnnotations) - { - AddODataAnnotations(resource.InstanceAnnotations, resourceContext, annotation); - } - } - - foreach(ODataProperty property in resource.Properties) - { - string propertyName = property.Name; - - if (property.InstanceAnnotations == null) - { - property.InstanceAnnotations = new List(); - } - - IDictionary propertyAnnotations = instanceAnnotationContainer.GetPropertyAnnotations(propertyName); - - if (propertyAnnotations != null) - { - foreach (KeyValuePair annotation in propertyAnnotations) - { - AddODataAnnotations(property.InstanceAnnotations, resourceContext, annotation); - } - } - } + delta = resourceContext.ResourceInstance as IDelta; } - } - private void AddODataAnnotations(ICollection InstanceAnnotations, ResourceContext resourceContext, KeyValuePair annotation) - { - ODataValue annotationValue = null; - - if (annotation.Value != null) + if (delta != null) { - IEdmTypeReference edmTypeReference = resourceContext.SerializerContext.GetEdmType(annotation.Value, - annotation.Value.GetType()); + if (instanceAnnotationInfo != null) + { + delta.TryGetPropertyValue(instanceAnnotationInfo.Name, out instanceAnnotations); + + } - ODataEdmTypeSerializer edmTypeSerializer = GetEdmTypeSerializer(edmTypeReference); + IDeltaSetItem deltaitem = resourceContext.ResourceInstance as IDeltaSetItem; - if (edmTypeSerializer != null) + if (deltaitem != null) { - annotationValue = edmTypeSerializer.CreateODataValue(annotation.Value, edmTypeReference, resourceContext.SerializerContext); + transientAnnotations = deltaitem.TransientInstanceAnnotationContainer; } } else { - annotationValue = new ODataNullValue(); - } - - if (annotationValue != null) - { - InstanceAnnotations.Add(new ODataInstanceAnnotation(annotation.Key, annotationValue)); - } - } + if (instanceAnnotationInfo == null || structuredObject == null || + !structuredObject.TryGetPropertyValue(instanceAnnotationInfo.Name, out instanceAnnotations) || instanceAnnotations == null) + { + edmEntityObject = structuredObject as EdmEntityObject; - private ODataEdmTypeSerializer GetEdmTypeSerializer(IEdmTypeReference edmTypeReference) - { - ODataEdmTypeSerializer edmTypeSerializer; - - if (edmTypeReference.IsCollection()) - { - edmTypeSerializer = new ODataCollectionSerializer(SerializerProvider, true); - } - else if (edmTypeReference.IsStructured()) - { - edmTypeSerializer = new ODataResourceValueSerializer(SerializerProvider); - } - else - { - edmTypeSerializer = SerializerProvider.GetEdmTypeSerializer(edmTypeReference); + if (edmEntityObject != null) + { + instanceAnnotations = edmEntityObject.PersistentInstanceAnnotationsContainer; + transientAnnotations = edmEntityObject.TransientInstanceAnnotationContainer; + } + } } - return edmTypeSerializer; + ODataSerializerHelper.AppendInstanceAnnotations(resource, resourceContext, instanceAnnotations, SerializerProvider); + + ODataSerializerHelper.AppendInstanceAnnotations(resource, resourceContext, transientAnnotations, SerializerProvider); } /// @@ -1345,6 +1392,42 @@ private IEnumerable> GetPro } } + private IEnumerable> GetNavigationPropertiesToWrite(SelectExpandNode selectExpandNode, ResourceContext resourceContext) + { + ISet navigationProperties = selectExpandNode.SelectedNavigationProperties; + + if (navigationProperties != null) + { + IEnumerable changedProperties = null; + if (null != resourceContext.EdmObject && resourceContext.EdmObject is IDelta changedObject) + { + changedProperties = changedObject.GetChangedPropertyNames(); + + foreach (IEdmNavigationProperty navigationProperty in navigationProperties) + { + if (changedProperties == null || changedProperties.Contains(navigationProperty.Name)) + { + yield return new KeyValuePair(navigationProperty, typeof(IEdmChangedObject)); + } + } + } + else if (null != resourceContext.ResourceInstance && resourceContext.ResourceInstance is IDelta deltaObject) + { + changedProperties = deltaObject.GetChangedPropertyNames(); + dynamic delta = deltaObject; + + foreach (IEdmNavigationProperty navigationProperty in navigationProperties) + { + Object obj = null; + if (changedProperties == null || changedProperties.Contains(navigationProperty.Name) && delta.DeltaNestedResources.TryGetValue(navigationProperty.Name, out obj)) + { + yield return new KeyValuePair(navigationProperty, obj.GetType()); + } + } + } + } + } + private void WriteExpandedNavigationProperties(SelectExpandNode selectExpandNode, ResourceContext resourceContext, ODataWriter writer) { Contract.Assert(resourceContext != null); @@ -1665,6 +1748,8 @@ private IEnumerable CreateStructuralPropertyBag(SelectExpandNode structuralProperties = structuralProperties.Where(p => changedProperties.Contains(p.Name)); } + bool isDeletedEntity = resourceContext.EdmObject is EdmDeltaDeletedEntityObject; + foreach (IEdmStructuralProperty structuralProperty in structuralProperties) { if (structuralProperty.Type != null && structuralProperty.Type.IsStream()) @@ -1674,10 +1759,12 @@ private IEnumerable CreateStructuralPropertyBag(SelectExpandNode } ODataProperty property = CreateStructuralProperty(structuralProperty, resourceContext); - if (property != null) + if (property == null || (isDeletedEntity && property.Value == null)) { - properties.Add(property); + continue; } + + properties.Add(property); } } @@ -1996,7 +2083,7 @@ private static IEdmStructuredType GetODataPathType(ODataSerializerContext serial } } - internal static void AddTypeNameAnnotationAsNeeded(ODataResource resource, IEdmStructuredType odataPathType, + internal static void AddTypeNameAnnotationAsNeeded(ODataResourceBase resource, IEdmStructuredType odataPathType, ODataMetadataLevel metadataLevel) { // ODataLib normally has the caller decide whether or not to serialize properties by leaving properties @@ -2021,7 +2108,7 @@ internal static void AddTypeNameAnnotationAsNeeded(ODataResource resource, IEdmS resource.TypeAnnotation = new ODataTypeAnnotation(typeName); } - internal static void AddTypeNameAnnotationAsNeededForComplex(ODataResource resource, ODataMetadataLevel metadataLevel) + internal static void AddTypeNameAnnotationAsNeededForComplex(ODataResourceBase resource, ODataMetadataLevel metadataLevel) { // ODataLib normally has the caller decide whether or not to serialize properties by leaving properties // null when values should not be serialized. The TypeName property is different and should always be @@ -2095,7 +2182,7 @@ internal static bool ShouldOmitOperation(IEdmOperation operation, OperationLinkB } } - internal static bool ShouldSuppressTypeNameSerialization(ODataResource resource, IEdmStructuredType edmType, + internal static bool ShouldSuppressTypeNameSerialization(ODataResourceBase resource, IEdmStructuredType edmType, ODataMetadataLevel metadataLevel) { Contract.Assert(resource != null); diff --git a/src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/ODataSerializerContext.cs b/src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/ODataSerializerContext.cs index f72ee955b8..8489ca30f4 100644 --- a/src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/ODataSerializerContext.cs +++ b/src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/ODataSerializerContext.cs @@ -27,6 +27,8 @@ public partial class ODataSerializerContext private ODataQueryContext _queryContext; private SelectExpandClause _selectExpandClause; private bool _isSelectExpandClauseSet; + private bool? _isUntyped; + private bool? _isDeltaOfT; /// /// Initializes a new instance of the class. @@ -163,6 +165,35 @@ internal ODataQueryContext QueryContext /// public ODataPath Path { get; set; } + internal Type Type { get; set; } + + internal bool IsUntyped + { + get + { + if (_isUntyped == null) + { + _isUntyped = typeof(IEdmObject).IsAssignableFrom(Type) || typeof(EdmChangedObjectCollection).IsAssignableFrom(Type); + } + + return _isUntyped.Value; + } + } + + internal bool IsDeltaOfT + { + get + { + if (_isDeltaOfT == null) + { + _isDeltaOfT = Type != null && TypeHelper.IsGenericType(Type) && (Type.GetGenericTypeDefinition() == typeof(DeltaSet<>) || + Type.GetGenericTypeDefinition() == typeof(Delta<>) || Type.GetGenericTypeDefinition() == typeof(DeltaDeletedEntityObject<>)); + } + + return _isDeltaOfT.Value; + } + } + /// /// Gets or sets the root element name which is used when writing primitive and enum types /// @@ -300,6 +331,11 @@ internal IEdmTypeReference GetEdmType(object instance, Type type) } else { + if (typeof(IDeltaSet).IsAssignableFrom(type)) + { + return EdmLibHelpers.ToEdmTypeReference(Path.EdmType, isNullable: false); + } + if (Model == null) { throw Error.InvalidOperation(SRResources.RequestMustHaveModel); @@ -312,7 +348,15 @@ internal IEdmTypeReference GetEdmType(object instance, Type type) { if (instance != null) { - edmType = _typeMappingCache.GetEdmType(instance.GetType(), Model); + TypedDelta delta = instance as TypedDelta; + if (delta != null) + { + edmType = _typeMappingCache.GetEdmType(delta.ExpectedClrType, Model); + } + else + { + edmType = _typeMappingCache.GetEdmType(instance.GetType(), Model); + } } if (edmType == null) diff --git a/src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/ODataSerializerHelper.cs b/src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/ODataSerializerHelper.cs new file mode 100644 index 0000000000..1600cca639 --- /dev/null +++ b/src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/ODataSerializerHelper.cs @@ -0,0 +1,113 @@ +//----------------------------------------------------------------------------- +// +// Copyright (c) .NET Foundation and Contributors. All rights reserved. +// See License.txt in the project root for license information. +// +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using Microsoft.AspNet.OData.Builder; +using Microsoft.OData; +using Microsoft.OData.Edm; + +namespace Microsoft.AspNet.OData.Formatter.Serialization +{ + /// + /// Helper class for OData Serialization + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1053:StaticHolderTypesShouldNotHaveConstructors")] + internal class ODataSerializerHelper + { + internal static void AppendInstanceAnnotations(ODataResourceBase resource, ResourceContext resourceContext, object value, ODataSerializerProvider SerializerProvider) + { + IODataInstanceAnnotationContainer instanceAnnotationContainer = value as IODataInstanceAnnotationContainer; + + if (instanceAnnotationContainer != null) + { + IDictionary clrAnnotations = instanceAnnotationContainer.GetResourceAnnotations(); + + if (clrAnnotations != null) + { + foreach (KeyValuePair annotation in clrAnnotations) + { + AddODataAnnotations(resource.InstanceAnnotations, resourceContext, annotation, SerializerProvider); + } + } + + if (resource.Properties != null) + { + foreach (ODataProperty property in resource.Properties) + { + string propertyName = property.Name; + + if (property.InstanceAnnotations == null) + { + property.InstanceAnnotations = new List(); + } + + IDictionary propertyAnnotations = instanceAnnotationContainer.GetPropertyAnnotations(propertyName); + + if (propertyAnnotations != null) + { + foreach (KeyValuePair annotation in propertyAnnotations) + { + AddODataAnnotations(property.InstanceAnnotations, resourceContext, annotation, SerializerProvider); + } + } + } + } + } + } + + + internal static void AddODataAnnotations(ICollection InstanceAnnotations, ResourceContext resourceContext, KeyValuePair annotation, ODataSerializerProvider SerializerProvider) + { + ODataValue annotationValue = null; + + if (annotation.Value != null) + { + IEdmTypeReference edmTypeReference = resourceContext.SerializerContext.GetEdmType(annotation.Value, + annotation.Value.GetType()); + + ODataEdmTypeSerializer edmTypeSerializer = GetEdmTypeSerializer(edmTypeReference, SerializerProvider); + + if (edmTypeSerializer != null) + { + annotationValue = edmTypeSerializer.CreateODataValue(annotation.Value, edmTypeReference, resourceContext.SerializerContext); + } + } + else + { + annotationValue = new ODataNullValue(); + } + + if (annotationValue != null) + { + InstanceAnnotations.Add(new ODataInstanceAnnotation(annotation.Key, annotationValue)); + } + } + + + private static ODataEdmTypeSerializer GetEdmTypeSerializer(IEdmTypeReference edmTypeReference, ODataSerializerProvider SerializerProvider) + { + ODataEdmTypeSerializer edmTypeSerializer; + + if (edmTypeReference.IsCollection()) + { + edmTypeSerializer = new ODataCollectionSerializer(SerializerProvider, true); + } + else if (edmTypeReference.IsStructured()) + { + edmTypeSerializer = new ODataResourceValueSerializer(SerializerProvider); + } + else + { + edmTypeSerializer = SerializerProvider.GetEdmTypeSerializer(edmTypeReference); + } + + return edmTypeSerializer; + } + + } +} diff --git a/src/Microsoft.AspNet.OData.Shared/Microsoft.AspNet.OData.Shared.projitems b/src/Microsoft.AspNet.OData.Shared/Microsoft.AspNet.OData.Shared.projitems index dce9ef5274..7c8741acdc 100644 --- a/src/Microsoft.AspNet.OData.Shared/Microsoft.AspNet.OData.Shared.projitems +++ b/src/Microsoft.AspNet.OData.Shared/Microsoft.AspNet.OData.Shared.projitems @@ -78,6 +78,7 @@ +