-
Notifications
You must be signed in to change notification settings - Fork 178
Description
Assemblies affected
Asp.NetCore OData 7.7.9
Describe the bug
V7 does not provide delta support for queries wih expanded collection navigation properties.
Data Model
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public virtual IList<Order> Orders { get; set; }
}
public class Order
{
public int Id { get; set; }
public int Amount { get; set; }
public string Title { get; set; }
[Contained]
public IList<ListItem> Items { get; set; }
}
public class ListItem
{
public int Id { get; set; }
public string Description { get; set; }
}
Reproduce steps
Consider above data model, where Customer and Order are EntitySets and ListItem is an EntityType.
Customer has Orders as collection-valued navigation property.
Orders has ListItem as contained collection-valued navigation property.
In this scenario, a Deep Delta for GetOrdersForCustomer or GetOrders with expanded Items with deltaToken fails for odata v7.
This seems to be supported only in odata library v9.
EDM (CSDL) Model
public static class EdmModelBuilder
{
private static IEdmModel _edmModel;
public static IEdmModel GetEdmModel()
{
if (_edmModel == null)
{
var builder = new ODataConventionModelBuilder();
builder.EntitySet<Customer>("Customers");
builder.EntitySet<Order>("Orders");
builder.EntityType<ListItem>();
_edmModel = builder.GetEdmModel();
}
return _edmModel;
}
}
Request/Response
Request:
GET {{DeepUpdateTests_9x_HostAddress}}/odata/Customers(1)/orders?$expand=items&$deltaToken=abcd
Response:
ODataException: Cannot transition from state 'NestedResourceInfoWithContent' to state 'DeltaResourceSet'. The only valid actions in state 'NestedResourceInfoWithContent' are to write a resource or a resource set'
Expected behavior
OData response serialization should support setting a changed object collection on an entity delta, as per documentation here: https://docs.oasis-open.org/odata/odata-json-format/v4.01/odata-json-format-v4.01.html#sec_AddedChangedEntity
{
"@odata.context": "http://localhost:5136/odata/$metadata#Orders/$delta",
"value": [
{
"Id": 121,
"Items@delta": [
{
"Id": 0,
"Description": "ChangedDescription"
}
]
},
{
"@odata.removed": {
"reason": "deleted"
},
"@odata.id": "http://localhost/odata/orders(3)",
"Id": 0
}
]
}
Additional Context
StackTrace:
ODataWriterCore.ValidateTransition(WriterState newState)
<>c.<EnterScope>b__183_0(ODataWriterCore thisParam, WriterState newStateParam)
ODataWriterCore.InterceptException[TArg0](Action`2 action, TArg0 arg0)
ODataWriterCore.EnterScope(WriterState newState, ODataItem item)
ODataWriterCore.WriteStartDeltaResourceSetImplementationAsync(ODataDeltaResourceSet deltaResourceSet)
ODataWriterCore.WriteStartAsync(ODataDeltaResourceSet deltaResourceSet)
ODataDeltaFeedSerializer.WriteFeedAsync(IEnumerable enumerable, IEdmTypeReference feedType, ODataWriter writer, ODataSerializerContext writeContext)
ODataDeltaFeedSerializer.WriteDeltaFeedInlineAsync(Object graph, IEdmTypeReference expectedType, ODataWriter writer, ODataSerializerContext writeContext)
ODataResourceSerializer.WriteDeltaComplexAndExpandedNavigationPropertyAsync(IEdmProperty edmProperty, SelectExpandClause selectExpandClause, ResourceContext resourceContext, ODataWriter writer, Type navigationPropertyType)
ODataResourceSerializer.WriteDeltaNavigationPropertiesAsync(SelectExpandNode selectExpandNode, ResourceContext resourceContext, ODataWriter writer)
ODataResourceSerializer.WriteDeltaResourceAsync(Object graph, ODataWriter writer, ODataSerializerContext writeContext)
ODataDeltaFeedSerializer.WriteFeedAsync(IEnumerable enumerable, IEdmTypeReference feedType, ODataWriter writer, ODataSerializerContext writeContext)
ODataDeltaFeedSerializer.WriteDeltaFeedInlineAsync(Object graph, IEdmTypeReference expectedType, ODataWriter writer, ODataSerializerContext writeContext)
ODataDeltaFeedSerializer.WriteObjectAsync(Object graph, Type type, ODataMessageWriter messageWriter, ODataSerializerContext writeContext)
ODataOutputFormatterHelper.WriteToStreamAsync(Type type, Object value, IEdmModel model, ODataVersion version, Uri baseAddress, MediaTypeHeaderValue contentType, IWebApiUrlHelper internaUrlHelper, IWebApiRequestMessage internalRequest, IWebApiHeaders internalRequestHeaders, Func`2 getODataMessageWrapper, Func`2 getEdmTypeSerializer, Func`2 getODataPayloadSerializer, Func`1 getODataSerializerContext)