From baeab9e2213c71705b13cde5c8a7abc0d466dc4b Mon Sep 17 00:00:00 2001 From: Sam Xu Date: Mon, 15 May 2023 22:44:23 -0700 Subject: [PATCH] fixes #926 Enable using ODataValue to write Untyped property directly And add test cases to verify the collection in collection --- .../Controllers/PeopleController.cs | 35 ++++++++--- .../Serialization/ODataResourceSerializer.cs | 18 +----- .../Microsoft.AspNetCore.OData.xml | 14 +++++ .../Untyped/UntypedDataSource.cs | 37 ++++++++++++ .../Untyped/UntypedTests.cs | 58 +++++++++++++++++++ .../ODataResourceSerializerTests.cs | 3 +- 6 files changed, 139 insertions(+), 26 deletions(-) diff --git a/sample/ODataRoutingSample/Controllers/PeopleController.cs b/sample/ODataRoutingSample/Controllers/PeopleController.cs index 502b373b5..ab9d77c68 100644 --- a/sample/ODataRoutingSample/Controllers/PeopleController.cs +++ b/sample/ODataRoutingSample/Controllers/PeopleController.cs @@ -157,12 +157,13 @@ public IActionResult Post([FromBody] Person person) { null, "A string Value", - // The following resources can't work: https://github.com/OData/odata.net/issues/2661 - // new Address { City = "Issaquah", Street = "Klahanie Way" }, - //new Person - //{ - // FirstName = "Kerry", LastName = "Xu" - //} + new Address { City = "Issaquah", Street = "Klahanie Way" }, + new Person + { + FirstName = "Kerry", LastName = "Xu", + Infos = new List { 1, 2, 3 }, // Collection should have a value. + Sources = new List { } // Collection should have a value. + } } }; @@ -217,7 +218,27 @@ public IActionResult Post([FromBody] Person person) }, [ null, - "A string Value" + "A string Value", + { + "City": "Issaquah", + "Street": "Klahanie Way" + }, + { + "@odata.type": "#ODataRoutingSample.Models.Person", + "@odata.id": "http://localhost:5000/People(FirstName='Kerry',LastName='Xu')", + "@odata.editLink": "People(FirstName='Kerry',LastName='Xu')", + "FirstName": "Kerry", + "LastName": "Xu", + "Data": null, + "Other": null, + "Infos": [ + 1, + 2, + 3 + ], + "Sources": [], + "CustomProperties": null + } ] ], "CustomProperties": { diff --git a/src/Microsoft.AspNetCore.OData/Formatter/Serialization/ODataResourceSerializer.cs b/src/Microsoft.AspNetCore.OData/Formatter/Serialization/ODataResourceSerializer.cs index d75222c6d..3bf527113 100644 --- a/src/Microsoft.AspNetCore.OData/Formatter/Serialization/ODataResourceSerializer.cs +++ b/src/Microsoft.AspNetCore.OData/Formatter/Serialization/ODataResourceSerializer.cs @@ -1332,23 +1332,7 @@ public virtual object CreateUntypedPropertyValue(IEdmStructuralProperty structur Error.Format(SRResources.TypeCannotBeSerialized, structuralProperty.Type.FullName())); } - ODataProperty odataProperty = - serializer.CreateProperty(propertyValue, actualType, structuralProperty.Name, writeContext); - - // The following codes are not-needed but ODL has the problem to write ODataPrimitiveValue for 'Edm.Untyped' declared property - // This's the workaround and when ODL fixes the issue at https://github.com/OData/odata.net/pull/2664, - // Let's update the dependency and remove these codes. - string untypedValue = ODataUriUtils.ConvertToUriLiteral(odataProperty.Value, ODataVersion.V4, resourceContext.EdmModel); - - return new ODataProperty - { - Name = odataProperty.Name, - Value = new ODataUntypedValue - { - RawValue = untypedValue, - TypeAnnotation = new ODataTypeAnnotation(actualType.FullName()) - } - }; + return serializer.CreateProperty(propertyValue, actualType, structuralProperty.Name, writeContext); } /// diff --git a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml index 6d2776d16..bcae30489 100644 --- a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml +++ b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml @@ -1102,6 +1102,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. diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/Untyped/UntypedDataSource.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/Untyped/UntypedDataSource.cs index 8b1af788c..e67545ac5 100644 --- a/test/Microsoft.AspNetCore.OData.E2E.Tests/Untyped/UntypedDataSource.cs +++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/Untyped/UntypedDataSource.cs @@ -147,6 +147,43 @@ public static IList GetAllPeople() } }, }; + + // Collection in collection + InModelPerson p = new InModelPerson + { + Id = 99, // special Id to test collection in collection + Name = "Chuan", + Data = new object[] + { + null, + new object[] { 42, new InModelAddress { City = "Redmond", Street = "134TH AVE" } } + }, + Infos = new object[] + { + new EdmUntypedCollection + { + new NotInModelAddress { ZipCode = "NoAValidZip", Location = "OnEarth" }, + null, + new EdmUntypedCollection + { + new EdmUntypedCollection + { + new object[] + { + new InModelAddress { City = "Issaquah", Street = "80TH ST" } + } + } + } + }, + 42 + }, + Containers = new Dictionary + { + { "Dp", new object[] { new InModelAddress{ City = "BlackCastle", Street = "To Castle Rd" } } } + } + }; + + _people.Add(p); } return _people; diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/Untyped/UntypedTests.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/Untyped/UntypedTests.cs index 777a23246..599898c67 100644 --- a/test/Microsoft.AspNetCore.OData.E2E.Tests/Untyped/UntypedTests.cs +++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/Untyped/UntypedTests.cs @@ -324,6 +324,64 @@ public async Task QuerySinglePeople_OnDeclaredUntypedProperty(string request, st Assert.Equal(expected, payloadBody); } + [Fact] + public async Task QuerySinglePeople_WithCollectionInCollection_OnDeclaredUntypedProperty() + { + // Arrange + HttpClient client = CreateClient(); + + // Act + HttpResponseMessage response = await client.GetAsync("odata/people/99"); + + // Assert + Assert.NotNull(response); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.NotNull(response.Content); + + string payloadBody = await response.Content.ReadAsStringAsync(); + + Assert.Equal("{\"@odata.context\":\"http://localhost/odata/$metadata#People/$entity\"," + + "\"Id\":99," + + "\"Name\":\"Chuan\"," + + "\"Data\":[" + + "null," + + "[" + + "42," + + "{" + + "\"@odata.type\":\"#Microsoft.AspNetCore.OData.E2E.Tests.Untyped.InModelAddress\"," + + "\"City\":\"Redmond\"," + + "\"Street\":\"134TH AVE\"" + + "}" + + "]" + + "]," + + "\"Infos\":[" + + "[" + + "{\"ZipCode\":\"NoAValidZip\",\"Location\":\"OnEarth\"}," + // a resource whose type is not defined in the Edm model, so there's no @odata.type + "null," + + "[" + + "[" + + "[" + + "{" + + "\"@odata.type\":\"#Microsoft.AspNetCore.OData.E2E.Tests.Untyped.InModelAddress\"," + + "\"City\":\"Issaquah\"," + + "\"Street\":\"80TH ST\"" + + "}" + + "]" + + "]" + + "]" + + "]," + + "42" + + "]," + + "\"Dp\":[" + + "{" + + "\"@odata.type\":\"#Microsoft.AspNetCore.OData.E2E.Tests.Untyped.InModelAddress\"," + + "\"City\":\"BlackCastle\"," + + "\"Street\":\"To Castle Rd\"" + + "}" + + "]" + + "}", payloadBody); + } + [Fact] public async Task CreatePerson_Works_RoundTrip() { diff --git a/test/Microsoft.AspNetCore.OData.Tests/Formatter/Serialization/ODataResourceSerializerTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Formatter/Serialization/ODataResourceSerializerTests.cs index 6369001b5..def38a42e 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Formatter/Serialization/ODataResourceSerializerTests.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Formatter/Serialization/ODataResourceSerializerTests.cs @@ -1388,8 +1388,7 @@ public void CreateUntypedPropertyValue_Calls_CreateODataValueOnInnerSerializer() innerSerializer.Verify(); ODataProperty odataProperty = Assert.IsType(createdProperty); Assert.Equal("PropertyName", odataProperty.Name); - ODataUntypedValue untypedValue = Assert.IsType(odataProperty.Value); - Assert.Equal("42", untypedValue.RawValue); + Assert.Equal(42, odataProperty.Value); } [Fact]