Search code examples
c#asp.net-web-api2odataodata-v4

Child collection empty when POSTing to OData v4 Web API action


I've got a parent object with a child collection property of complex objects. When performing a POST to create a new record of this parent & it's related children, I can see in the Post() action that the parent is deserialized correctly, but that the child collection is empty.

The API project uses the following package versions for Web API & OData:
Microsoft.AspNet.OData - v7.1.0
Microsoft.AspNet.WebApi - v5.2.7

Example models:

public class Parent
{
    public Parent()
    {
        Children = new List<Child>();
    }

    public int Id { get; set; }
    public string Name { get; set; }
    public List<Child> Children { get; set; }
}

public class Child
{
    public int Id { get; set; }
    public int ParentId { get; set; }
    public string Name { get; set; }
}

OData configuration

public static void Register(HttpConfiguration config)
{
    var builder = new ODataConventionModelBuilder();
    builder.Namespace = "NS";

    builder.EntitySet<Parent>("Parents")
        .EntityType
        .Expand(SelectExpandType.Automatic)
        .Filter().OrderBy().Select().Page();

    config.MapODataServiceRoute("ODataRoute", null, builder.GetEdmModel());
}

Controller definition

public class ParentsController : ODataController
{
    public async Task<IHttpActionResult> Post(Parent model)
    {
        // process data from 'model' parameter here
    }
}

It's inside this Post() controller action that the collection is empty - i.e. model.Children.Length is equal to zero.

On the client-side, I'm using the OData v4 Client Code Generator (v7.5.1) extension to generate client proxy classes from the API metadata. The below snippet shows how the proxy objects are being created & passed to the API endpoint:

var apiModel = new Parent();
apiModel.Children.Load(new List<Child> { new Child { Name = "Test Child" } })

Container.AddToParents(apiModel);
await Container.SaveChangesAsync();

I can confirm that the child objects are being passed to the API. It just seems like the API is not deserializing these into the collection for the action parameter. Anyone know what may be causing this to happen? Any help would be greatly appreciated.

Update
Turns out the child collection wasn't actually get passed. The traffic I captured in Fiddler that showed the child array was getting fired by some errant test code in my client & not the OData v4 Client Code Generator proxy.


Solution

  • The issue is that the proxy generated by OData v4 Client Code Generator does not support deep inserts. This Stackoverflow.com question gave me the correct terminology to search for, as it was answered in 2017 & I was looking to see if support has been added. Sadly it doesn't appear so, judging by this open issue on the team's GitHub.

    For now, the only options seems to be to create the parent, then all the children, as two separate calls, or switch to a different library like RestSharp.