diff --git a/src/Microsoft.AspNetCore.OData/Formatter/Serialization/ODataResourceSerializer.cs b/src/Microsoft.AspNetCore.OData/Formatter/Serialization/ODataResourceSerializer.cs index 297e152e4..cd689a6eb 100644 --- a/src/Microsoft.AspNetCore.OData/Formatter/Serialization/ODataResourceSerializer.cs +++ b/src/Microsoft.AspNetCore.OData/Formatter/Serialization/ODataResourceSerializer.cs @@ -749,16 +749,16 @@ private async Task WriteDynamicComplexPropertiesAsync(ResourceContext resourceCo if (edmTypeReference.IsStructured() || (edmTypeReference.IsCollection() && edmTypeReference.AsCollection().ElementType().IsStructured())) { - ODataNestedResourceInfo nestedResourceInfo = new ODataNestedResourceInfo - { - IsCollection = edmTypeReference.IsCollection(), - Name = dynamicComplexProperty.Key, - }; + ODataNestedResourceInfo nestedResourceInfo + = CreateDynamicComplexNestedResourceInfo(dynamicComplexProperty.Key, dynamicComplexProperty.Value, edmTypeReference, resourceContext); - await writer.WriteStartAsync(nestedResourceInfo).ConfigureAwait(false); - await WriteDynamicComplexPropertyAsync(dynamicComplexProperty.Value, edmTypeReference, resourceContext, writer) - .ConfigureAwait(false); - await writer.WriteEndAsync().ConfigureAwait(false); + if (nestedResourceInfo != null) + { + await writer.WriteStartAsync(nestedResourceInfo).ConfigureAwait(false); + await WriteDynamicComplexPropertyAsync(dynamicComplexProperty.Value, edmTypeReference, resourceContext, writer) + .ConfigureAwait(false); + await writer.WriteEndAsync().ConfigureAwait(false); + } } } } @@ -834,16 +834,14 @@ private async Task WriteComplexPropertiesAsync(SelectExpandNode selectExpandNode { IEdmStructuralProperty complexProperty = selectedComplex.Key; - ODataNestedResourceInfo nestedResourceInfo = new ODataNestedResourceInfo + ODataNestedResourceInfo nestedResourceInfo = CreateComplexNestedResourceInfo(complexProperty, selectedComplex.Value, resourceContext); + if (nestedResourceInfo != null) { - IsCollection = complexProperty.Type.IsCollection(), - Name = complexProperty.Name - }; - - await writer.WriteStartAsync(nestedResourceInfo).ConfigureAwait(false); - await WriteComplexAndExpandedNavigationPropertyAsync(complexProperty, selectedComplex.Value, resourceContext, writer) - .ConfigureAwait(false); - await writer.WriteEndAsync().ConfigureAwait(false); + await writer.WriteStartAsync(nestedResourceInfo).ConfigureAwait(false); + await WriteComplexAndExpandedNavigationPropertyAsync(complexProperty, selectedComplex.Value, resourceContext, writer) + .ConfigureAwait(false); + await writer.WriteEndAsync().ConfigureAwait(false); + } } } @@ -960,6 +958,59 @@ private IEnumerable CreateNavigationLinks( } } + /// + /// Creates the to be written while writing this dynamic complex property. + /// + /// The dynamic property name. + /// The dynamic property value. + /// The edm type reference. + /// The context for the complex instance being written. + /// The nested resource info to be written. Returns 'null' will omit this serialization. + /// It enables customer to get more control by overriding this method. + public virtual ODataNestedResourceInfo CreateDynamicComplexNestedResourceInfo(string propertyName, object propertyValue, IEdmTypeReference edmType, ResourceContext resourceContext) + { + ODataNestedResourceInfo nestedInfo = null; + if (propertyName != null && edmType != null) + { + nestedInfo = new ODataNestedResourceInfo + { + IsCollection = edmType.IsCollection(), + Name = propertyName, + }; + } + + return nestedInfo; + } + + /// + /// Creates the to be written while writing this complex property. + /// + /// The complex property for which the nested resource info is being created. + /// The corresponding sub select item belongs to this complex property. + /// The context for the complex instance being written. + /// The nested resource info to be written. Returns 'null' will omit this complex serialization. + /// It enables customer to get more control by overriding this method. + public virtual ODataNestedResourceInfo CreateComplexNestedResourceInfo(IEdmStructuralProperty complexProperty, PathSelectItem pathSelectItem, ResourceContext resourceContext) + { + if (complexProperty == null) + { + throw Error.ArgumentNull(nameof(complexProperty)); + } + + ODataNestedResourceInfo nestedInfo = null; + + if (complexProperty.Type != null) + { + nestedInfo = new ODataNestedResourceInfo + { + IsCollection = complexProperty.Type.IsCollection(), + Name = complexProperty.Name + }; + } + + return nestedInfo; + } + /// /// Creates the to be written while writing this entity. /// diff --git a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml index dc0971029..20212a41a 100644 --- a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml +++ b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml @@ -1094,6 +1094,20 @@ The type to test. True if the type is a DateTime; false otherwise. + + + Determine if a type is a . + + The type to test. + True if the type is a DateOnly; false otherwise. + + + + Determine if a type is a . + + The type to test. + True if the type is a TimeOnly; false otherwise. + Determine if a type is a TimeSpan. @@ -4606,6 +4620,27 @@ Write the navigation link for the select navigation properties. + + + Creates the to be written while writing this dynamic complex property. + + The dynamic property name. + The dynamic property value. + The edm type reference. + The context for the complex instance being written. + The nested resource info to be written. Returns 'null' will omit this serialization. + It enables customer to get more controll by overriding this method. + + + + Creates the to be written while writing this complex property. + + The complex property for which the nested resource info is being created. + The corresponding sub select item belongs to this complex property. + The context for the complex instance being written. + The nested resource info to be written. Returns 'null' will omit this complex serialization. + It enables customer to get more controll by overriding this method. + Creates the to be written while writing this entity. @@ -14382,19 +14417,3 @@ -ummary> - The value segment. - - - - Gets the value segment. - - - - - - - - - - diff --git a/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt b/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt index 3ee9900ec..30f0038b1 100644 --- a/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt +++ b/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt @@ -1736,7 +1736,9 @@ virtual Microsoft.AspNetCore.OData.Formatter.Serialization.ODataEdmTypeSerialize virtual Microsoft.AspNetCore.OData.Formatter.Serialization.ODataEnumSerializer.CreateODataEnumValue(object graph, Microsoft.OData.Edm.IEdmEnumTypeReference enumType, Microsoft.AspNetCore.OData.Formatter.Serialization.ODataSerializerContext writeContext) -> Microsoft.OData.ODataEnumValue virtual Microsoft.AspNetCore.OData.Formatter.Serialization.ODataPrimitiveSerializer.CreateODataPrimitiveValue(object graph, Microsoft.OData.Edm.IEdmPrimitiveTypeReference primitiveType, Microsoft.AspNetCore.OData.Formatter.Serialization.ODataSerializerContext writeContext) -> Microsoft.OData.ODataPrimitiveValue virtual Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSerializer.AppendDynamicProperties(Microsoft.OData.ODataResource resource, Microsoft.AspNetCore.OData.Formatter.Serialization.SelectExpandNode selectExpandNode, Microsoft.AspNetCore.OData.Formatter.ResourceContext resourceContext) -> void +virtual Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSerializer.CreateComplexNestedResourceInfo(Microsoft.OData.Edm.IEdmStructuralProperty complexProperty, Microsoft.OData.UriParser.PathSelectItem pathSelectItem, Microsoft.AspNetCore.OData.Formatter.ResourceContext resourceContext) -> Microsoft.OData.ODataNestedResourceInfo virtual Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSerializer.CreateComputedProperty(string propertyName, Microsoft.AspNetCore.OData.Formatter.ResourceContext resourceContext) -> Microsoft.OData.ODataProperty +virtual Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSerializer.CreateDynamicComplexNestedResourceInfo(string propertyName, object propertyValue, Microsoft.OData.Edm.IEdmTypeReference edmType, Microsoft.AspNetCore.OData.Formatter.ResourceContext resourceContext) -> Microsoft.OData.ODataNestedResourceInfo virtual Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSerializer.CreateETag(Microsoft.AspNetCore.OData.Formatter.ResourceContext resourceContext) -> string virtual Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSerializer.CreateNavigationLink(Microsoft.OData.Edm.IEdmNavigationProperty navigationProperty, Microsoft.AspNetCore.OData.Formatter.ResourceContext resourceContext) -> Microsoft.OData.ODataNestedResourceInfo virtual Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSerializer.CreateODataAction(Microsoft.OData.Edm.IEdmAction action, Microsoft.AspNetCore.OData.Formatter.ResourceContext resourceContext) -> Microsoft.OData.ODataAction diff --git a/test/Microsoft.AspNetCore.OData.Tests/Formatter/Serialization/ODataResourceSerializerTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Formatter/Serialization/ODataResourceSerializerTests.cs index 8e726548d..ba98c0d98 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Formatter/Serialization/ODataResourceSerializerTests.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Formatter/Serialization/ODataResourceSerializerTests.cs @@ -234,6 +234,34 @@ public async Task WriteObjectInlineAsync_WritesODataResourceFrom_CreateResource( writer.Verify(); } + [Fact] + public async Task WriteObjectInlineAsync_Calls_CreateComplexNestedResourceInfo_ForEachSelectedComplexProperty() + { + // Arrange + SelectExpandNode selectExpandNode = new SelectExpandNode + { + SelectedComplexProperties = new Dictionary + { + { new Mock().Object, null }, + { new Mock().Object, null } + } + }; + + Mock writer = new Mock(); + Mock serializer = new Mock(_serializerProvider); + serializer.Setup(s => s.CreateSelectExpandNode(It.IsAny())).Returns(selectExpandNode); + serializer.CallBase = true; + + serializer.Setup(s => s.CreateComplexNestedResourceInfo(selectExpandNode.SelectedComplexProperties.ElementAt(0).Key, null, It.IsAny())).Verifiable(); + serializer.Setup(s => s.CreateComplexNestedResourceInfo(selectExpandNode.SelectedComplexProperties.ElementAt(1).Key, null, It.IsAny())).Verifiable(); + + // Act + await serializer.Object.WriteObjectInlineAsync(_customer, _customerType, writer.Object, _writeContext); + + // Assert + serializer.Verify(); + } + [Fact] public async Task WriteObjectInlineAsync_Calls_CreateNavigationLink_ForEachSelectedNavigationProperty() { diff --git a/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.Net5.bsl b/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.Net5.bsl index e70b51040..a8e204cae 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.Net5.bsl +++ b/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.Net5.bsl @@ -2216,7 +2216,9 @@ public class Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSer public ODataResourceSerializer (Microsoft.AspNetCore.OData.Formatter.Serialization.IODataSerializerProvider serializerProvider) public virtual void AppendDynamicProperties (Microsoft.OData.ODataResource resource, Microsoft.AspNetCore.OData.Formatter.Serialization.SelectExpandNode selectExpandNode, Microsoft.AspNetCore.OData.Formatter.ResourceContext resourceContext) + public virtual Microsoft.OData.ODataNestedResourceInfo CreateComplexNestedResourceInfo (Microsoft.OData.Edm.IEdmStructuralProperty complexProperty, Microsoft.OData.UriParser.PathSelectItem pathSelectItem, Microsoft.AspNetCore.OData.Formatter.ResourceContext resourceContext) public virtual Microsoft.OData.ODataProperty CreateComputedProperty (string propertyName, Microsoft.AspNetCore.OData.Formatter.ResourceContext resourceContext) + public virtual Microsoft.OData.ODataNestedResourceInfo CreateDynamicComplexNestedResourceInfo (string propertyName, object propertyValue, Microsoft.OData.Edm.IEdmTypeReference edmType, Microsoft.AspNetCore.OData.Formatter.ResourceContext resourceContext) public virtual string CreateETag (Microsoft.AspNetCore.OData.Formatter.ResourceContext resourceContext) public virtual Microsoft.OData.ODataNestedResourceInfo CreateNavigationLink (Microsoft.OData.Edm.IEdmNavigationProperty navigationProperty, Microsoft.AspNetCore.OData.Formatter.ResourceContext resourceContext) public virtual Microsoft.OData.ODataAction CreateODataAction (Microsoft.OData.Edm.IEdmAction action, Microsoft.AspNetCore.OData.Formatter.ResourceContext resourceContext) diff --git a/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.Net6.bsl b/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.Net6.bsl index e70b51040..a8e204cae 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.Net6.bsl +++ b/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.Net6.bsl @@ -2216,7 +2216,9 @@ public class Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSer public ODataResourceSerializer (Microsoft.AspNetCore.OData.Formatter.Serialization.IODataSerializerProvider serializerProvider) public virtual void AppendDynamicProperties (Microsoft.OData.ODataResource resource, Microsoft.AspNetCore.OData.Formatter.Serialization.SelectExpandNode selectExpandNode, Microsoft.AspNetCore.OData.Formatter.ResourceContext resourceContext) + public virtual Microsoft.OData.ODataNestedResourceInfo CreateComplexNestedResourceInfo (Microsoft.OData.Edm.IEdmStructuralProperty complexProperty, Microsoft.OData.UriParser.PathSelectItem pathSelectItem, Microsoft.AspNetCore.OData.Formatter.ResourceContext resourceContext) public virtual Microsoft.OData.ODataProperty CreateComputedProperty (string propertyName, Microsoft.AspNetCore.OData.Formatter.ResourceContext resourceContext) + public virtual Microsoft.OData.ODataNestedResourceInfo CreateDynamicComplexNestedResourceInfo (string propertyName, object propertyValue, Microsoft.OData.Edm.IEdmTypeReference edmType, Microsoft.AspNetCore.OData.Formatter.ResourceContext resourceContext) public virtual string CreateETag (Microsoft.AspNetCore.OData.Formatter.ResourceContext resourceContext) public virtual Microsoft.OData.ODataNestedResourceInfo CreateNavigationLink (Microsoft.OData.Edm.IEdmNavigationProperty navigationProperty, Microsoft.AspNetCore.OData.Formatter.ResourceContext resourceContext) public virtual Microsoft.OData.ODataAction CreateODataAction (Microsoft.OData.Edm.IEdmAction action, Microsoft.AspNetCore.OData.Formatter.ResourceContext resourceContext) diff --git a/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.NetCore31.bsl b/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.NetCore31.bsl index e70b51040..a8e204cae 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.NetCore31.bsl +++ b/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.NetCore31.bsl @@ -2216,7 +2216,9 @@ public class Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSer public ODataResourceSerializer (Microsoft.AspNetCore.OData.Formatter.Serialization.IODataSerializerProvider serializerProvider) public virtual void AppendDynamicProperties (Microsoft.OData.ODataResource resource, Microsoft.AspNetCore.OData.Formatter.Serialization.SelectExpandNode selectExpandNode, Microsoft.AspNetCore.OData.Formatter.ResourceContext resourceContext) + public virtual Microsoft.OData.ODataNestedResourceInfo CreateComplexNestedResourceInfo (Microsoft.OData.Edm.IEdmStructuralProperty complexProperty, Microsoft.OData.UriParser.PathSelectItem pathSelectItem, Microsoft.AspNetCore.OData.Formatter.ResourceContext resourceContext) public virtual Microsoft.OData.ODataProperty CreateComputedProperty (string propertyName, Microsoft.AspNetCore.OData.Formatter.ResourceContext resourceContext) + public virtual Microsoft.OData.ODataNestedResourceInfo CreateDynamicComplexNestedResourceInfo (string propertyName, object propertyValue, Microsoft.OData.Edm.IEdmTypeReference edmType, Microsoft.AspNetCore.OData.Formatter.ResourceContext resourceContext) public virtual string CreateETag (Microsoft.AspNetCore.OData.Formatter.ResourceContext resourceContext) public virtual Microsoft.OData.ODataNestedResourceInfo CreateNavigationLink (Microsoft.OData.Edm.IEdmNavigationProperty navigationProperty, Microsoft.AspNetCore.OData.Formatter.ResourceContext resourceContext) public virtual Microsoft.OData.ODataAction CreateODataAction (Microsoft.OData.Edm.IEdmAction action, Microsoft.AspNetCore.OData.Formatter.ResourceContext resourceContext)