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

OData V4 client adding child entity


I have a parent (Order) and child (OrderDetail) where Order already exists in the database and OrderDetail also exists in the database.
All I really want to do is add another OrderDetail record bound to the Order.

I have been down several paths and I'm not even sure what is the correct path.
Let's make some assumptions that navigations between these are already working.
I can $expand=OrderDetails fine and I can also Orders(1)/OrderDetails fine and do the reverse from OrderDetails.

Based on this Updating the Data Service, all I need to do is call AddRelatedObject and then Add the object to the OrderDetails collection.

// Add the new item with a link to the related Order
context.AddRelatedObject(order, "OrderDetails", newOrderDetail);
// Add the new order detail to the collection
order.Order_Details.Add(newOrderDetail);
newOrderDetail.Order = order;

Seems simple enough.
Yet when I execute context.SaveChanges(SaveChangesOptions.ReplaceOnUpdate) it will throw an error.

{"error":{"code":"","message":"No HTTP resource was found that matches the request URI 'http://localhost/Test/odata/Orders(1)/OrderDetails'.","innererror":{"message":"No routing convention was found to select an action for the OData path with template '~/entityset/key/navigation'.","type":"","stacktrace":""}}}

But if I navigate to the URL listed, it shows data.
Time for Fiddler.
In Fiddler, I can see this is a POST to the URL and not a GET.
Which it should be a POST but not to the URL listed.
The POST should have been to /odata/OrderDetails

Round 2

// Add the new item with a link to the related Order
context.AttachTo("OrderDetails", newOrderDetail);
// Add a link between Order and the new OrderDetail
context.AddLink(order, "OrderDetails", newOrderDetail);
// Add the new order detail to the collection
order.Order_Details.Add(newOrderDetail);
newOrderDetail.Order = order;

Still a POST with an error but the URL is slightly different and the json posted only has "/odata/OrderDetail(0)" it also now has "$ref".

{"error":{"code":"","message":"No HTTP resource was found that matches the request URI 'http://localhost/Test/odata/Orders(1)/OrderDetails/$ref'.","innererror":{"message":"No routing convention was found to select an action for the OData path with template '~/entityset/key/navigation/$ref'.","type":"","stacktrace":""}}}

Well a quick web search led me to this article Entity Relations in OData v4 Using ASP.NET Web API 2.2
This article says I need to add a "CreateRef" in the Orders controller.
I did create a "CreateRef" in the Orders controller and sure enough it gets called BUT the article assumes the OrderDetail exists in the database.
It is not posting the json OrderDetail object.

Round 3

// Add the new item with a link to the related Order
context.AttachTo("OrderDetails", newOrderDetail);
// Attach a link between Order and the new OrderDetail
context.AttachLink(order, "OrderDetails", newOrderDetail);
// Add the new order detail to the collection
order.Order_Details.Add(newOrderDetail);
newOrderDetail.Order = order;

Well this seems much better.
No error but it did not fully work.
It sent a PUT to the /odata/OrderDetails(0) and it did send the json OrderDetail object BUT this should have been a POST not a PUT.

I feel I am so close yet I can't seem to figure out how to get it to work properly.

Any ideas?


Solution

  • After trial and error I found something that worked.

    // create a new order detail
    OrderDetail newOrderDetail = new OrderDetail();
    // set the orderID on the new order detail
    newOrderDetail.OrderID = order.ID;
    // add the order back as a link on the order detail
    newOrderDetail.Order = order;
    // add the order detail to the order detail collection on the order
    order.OrderDetails.Add(newOrderDetail);
    // add the order detail to the context
    context.AddToOrderDetail(newOrderDetail);
    // now update context for the order
    context.UpdateObject(order);
    // now save
    context.SaveChanges();