Skip to content

Commit

Permalink
Fix serialization issue affecting nested complex property (#755)
Browse files Browse the repository at this point in the history
  • Loading branch information
gathogojr authored Nov 30, 2022
1 parent 61ae323 commit 2052852
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ await serializer.WriteDeltaObjectInlineAsync(propertyValue, edmProperty.Type, wr
}
}

private static IEnumerable<ODataProperty> CreateODataPropertiesFromDynamicType(EdmEntityType entityType, object graph,
private static IEnumerable<ODataProperty> CreateODataPropertiesFromDynamicType(EdmStructuredType structuredType, object graph,
Dictionary<IEdmProperty, object> dynamicTypeProperties, ODataSerializerContext writeContext)
{
Contract.Assert(dynamicTypeProperties != null);
Expand All @@ -267,7 +267,7 @@ private static IEnumerable<ODataProperty> CreateODataPropertiesFromDynamicType(E
{
foreach (var prop in dynamicObject.Values)
{
IEdmProperty edmProperty = entityType?.Properties()
IEdmProperty edmProperty = structuredType?.Properties()
.FirstOrDefault(p => p.Name.Equals(prop.Key, StringComparison.Ordinal));

if (prop.Value != null
Expand Down Expand Up @@ -321,21 +321,21 @@ private async Task WriteDynamicTypeResourceAsync(object graph, ODataWriter write
ODataSerializerContext writeContext)
{
var dynamicTypeProperties = new Dictionary<IEdmProperty, object>();
var entityType = expectedType.Definition as EdmEntityType;
var structuredType = expectedType.Definition as EdmStructuredType;
var resource = new ODataResource()
{
TypeName = expectedType.FullName(),
Properties = CreateODataPropertiesFromDynamicType(entityType, graph, dynamicTypeProperties, writeContext)
Properties = CreateODataPropertiesFromDynamicType(structuredType, graph, dynamicTypeProperties, writeContext)
};

resource.IsTransient = true;
await writer.WriteStartAsync(resource).ConfigureAwait(false);
foreach (var property in dynamicTypeProperties.Keys)
{
var resourceContext = new ResourceContext(writeContext, expectedType.AsEntity(), graph);
if (entityType.NavigationProperties().Any(p => p.Type.Equals(property.Type)) && !(property.Type is EdmCollectionTypeReference))
var resourceContext = new ResourceContext(writeContext, expectedType.AsStructured(), graph);
if (structuredType.NavigationProperties().Any(p => p.Type.Equals(property.Type)) && !(property.Type is EdmCollectionTypeReference))
{
var navigationProperty = entityType.NavigationProperties().FirstOrDefault(p => p.Type.Equals(property.Type));
var navigationProperty = structuredType.NavigationProperties().FirstOrDefault(p => p.Type.Equals(property.Type));
var navigationLink = CreateNavigationLink(navigationProperty, resourceContext);
if (navigationLink != null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,32 @@ public void Generate()
_context.SaveChanges();
}
}

public class EmployeesController : ODataController
{
private static readonly List<Employee> employees = new List<Employee>
{
new Employee
{
Id = 1,
NextOfKin = new NextOfKin { Name = "NoK 1", PhysicalAddress = new Location { City = "Redmond" } }
},
new Employee
{
Id = 2,
NextOfKin = new NextOfKin { Name = "NoK 2", PhysicalAddress = new Location { City = "Nairobi" } }
},
new Employee
{
Id = 3,
NextOfKin = new NextOfKin { Name = "NoK 3", PhysicalAddress = new Location { City = "Redmond" } }
}
};

[EnableQuery]
public IQueryable<Employee> Get()
{
return employees.AsQueryable();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,21 @@ public class Address

public string Street { get; set; }
}

public class Employee
{
public int Id { get; set; }
public NextOfKin NextOfKin { get; set; }
}

public class NextOfKin
{
public string Name { get; set; }
public Location PhysicalAddress { get; set; }
}

public class Location
{
public string City { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OData.Edm;
using Microsoft.OData.ModelBuilder;
using Newtonsoft.Json.Linq;
using Xunit;

Expand Down Expand Up @@ -202,4 +203,70 @@ public async Task AggregationOnEntitySetWorksWithGroupby()
Assert.Equal(2 * (25 + 75), customerOnePrice);
}
}

public class NestedComplexPropertyAggregationTests : WebApiTestBase<NestedComplexPropertyAggregationTests>
{
public NestedComplexPropertyAggregationTests(WebApiTestFixture<NestedComplexPropertyAggregationTests> fixture)
: base(fixture)
{
}

protected static void UpdateConfigureServices(IServiceCollection services)
{
var builder = new ODataConventionModelBuilder();
builder.EntitySet<Employee>("Employees");

services.ConfigureControllers(typeof(EmployeesController));

services.AddControllers().AddOData(options => options.Select().Filter().OrderBy().Expand().Count().SkipToken().SetMaxTop(null)
.AddRouteComponents("aggregation", builder.GetEdmModel()));
}

private const string AggregationTestBaseUrl = "aggregation/Employees";

[Fact]
public async Task GroupByComplexProperty()
{
// Arrange
string queryUrl = AggregationTestBaseUrl + "?$apply=groupby((NextOfKin/Name))";

HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, queryUrl);
request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json;odata.metadata=none"));
HttpClient client = this.CreateClient();

// Act
HttpResponseMessage response = await client.SendAsync(request);

// Assert
var result = await response.Content.ReadAsObject<JObject>();
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var results = result["value"] as JArray;
Assert.Equal(3, results.Count);
Assert.Equal("NoK 1", (results[0]["NextOfKin"] as JObject)["Name"].ToString());
Assert.Equal("NoK 2", (results[1]["NextOfKin"] as JObject)["Name"].ToString());
Assert.Equal("NoK 3", (results[2]["NextOfKin"] as JObject)["Name"].ToString());
}

[Fact]
public async Task GroupByNestedComplexProperty()
{
// Arrange
string queryUrl = AggregationTestBaseUrl + "?$apply=groupby((NextOfKin/PhysicalAddress/City))";

HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, queryUrl);
request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json;odata.metadata=none"));
HttpClient client = this.CreateClient();

// Act
HttpResponseMessage response = await client.SendAsync(request);

// Assert
var result = await response.Content.ReadAsObject<JObject>();
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var results = result["value"] as JArray;
Assert.Equal(2, results.Count);
Assert.Equal("Redmond", ((results[0]["NextOfKin"] as JObject)["PhysicalAddress"] as JObject)["City"].ToString());
Assert.Equal("Nairobi", ((results[1]["NextOfKin"] as JObject)["PhysicalAddress"] as JObject)["City"].ToString());
}
}
}

0 comments on commit 2052852

Please sign in to comment.