How do I update an OData entity and modify its navigation properties in one request?
Asked Answered
T

3

8

I am trying to implement what I thought was a simple scenario using an OData service provided by WCF Data services (using the OData V3 application/json;odata=verbose payload format, for now. I may use the JSON Light format in the future). The basic scenario goes like this:

I have two entities:

class Person 
{ 
  public int ID { get; set; }
  public string Name { get; set; } 
  public virtual PersonCategory Category { get; set; }
}

class PersonCategory
{
  public int ID { get; set; }
  public string Description { get; set; }
  public virtual ICollection<Person> People { get; set; }
}

Now, I want to create a simple edit page for a Person. This edit page might have an input for the Name, and an input or drop-down for the Category of the Person.

So, the scenario goes:

  1. Code downloads the Person using $expand for the Category: GET /api.svc/People(1)?$expand=Category
  2. User edits both the person's Name property and their Category.
  3. Code for the page makes a single request to update that Person's Name and Category properties.

The key here is in "a single request". This is the part that I'm having trouble finding documentation for. I've seen examples where they split number 3 above into two requests. Something like this (I don't remember the exact format - I'm also not sure if you'd have to DELETE the Category link before doing the PUT):

PATCH /api.svc/People(1) with content: {"Name": "new name" }
PUT /api.svc/People(1)/$links/Category with content: { "url": "/api.svc/Categories(2)" }

But, I've also heard it said, but not demonstrated, that it's possible to implement this update as a single request with the change to the Category navigation property specified inline with the other changes to the Person entity. Could someone give me an example of how this might be done? Also, can you show me how it would be done with a many-to-many navigation property, as opposed to the one-to-many I've described above.

And finally, I'm currently using the verbose JSON format, V3. Would your answers to the questions above be different if I instead used the new JSON light format? If so, how?

Tired answered 26/9, 2012 at 14:59 Comment(5)
You could send a batch request - it would be a single request that contains both operation.Bascio
Is there no other way to do this other than a batch request? That would add some additional complexity to the round-tripping code on the client side that I'd prefer to avoid. Ideally I'd like to just format my PATCH /api/Activity(1) request to be something like: {"category": { "__deferred" : { "uri": "/api.svc/Categories(2)" }}} This doesn't appear to work however. I get a 204 response, but the property remains unchanged on the server side.Tired
So called deep updates (which is what you want here) are not supported by WCF Data Services. In fact even OData itself doesn't really define it. It can get rather complicated as to what it means in some cases. I'm afraid batch is the only solution for this. Deep inserts are supported (but again only inserts, not modifying anything existing) though.Denishadenison
Question: Do you want to update the category instance or do you want to update some of the properties of the category instance. There is no way to do the later other than batch. For the former, you can do something like: { "Name" : "new name", "Category" : { "__metadata" : { "uri" : "/api.svc/Categories(2)" }}}. Hope this helps.Burglar
Sorry for the late reply. Yes, the former one. I don't want to update properties of the category instance - just update the category of the Person instance to be a different category.Tired
T
4

Pratik's comment was the answer (Pratik if you'd like to repost this as an answer, I'll mark it as such - thanks!):

Question: Do you want to update the category instance or do you want to update some of the properties of the category instance. There is no way to do the later other than batch. For the former, you can do something like: { "Name" : "new name", "Category" : { "__metadata" : { "uri" : "/api.svc/Categories(2)" }}}. Hope this helps. – Pratik

Tired answered 3/12, 2012 at 19:47 Comment(1)
Hmm does work when I use the full url\uri for the link metadata, ok then.Dunstan
T
5

I found two ways to represent navigation properties inline:

application/json;odata=verbose - { "Name": "new name", "Category": { "__metadata": { "uri": "Categories(2)" }}}

application/json - { "Name": "new name", "[email protected]": "Categories(2)" }

Toddler answered 24/10, 2013 at 7:51 Comment(1)
Your first example is the OData 3.0 spec. Your second is OData 4.0Vatican
T
4

Pratik's comment was the answer (Pratik if you'd like to repost this as an answer, I'll mark it as such - thanks!):

Question: Do you want to update the category instance or do you want to update some of the properties of the category instance. There is no way to do the later other than batch. For the former, you can do something like: { "Name" : "new name", "Category" : { "__metadata" : { "uri" : "/api.svc/Categories(2)" }}}. Hope this helps. – Pratik

Tired answered 3/12, 2012 at 19:47 Comment(1)
Hmm does work when I use the full url\uri for the link metadata, ok then.Dunstan
P
0

You don't need a batch, anymore. You can do it in one call. You simply need to also send up the changed properties and let the repository handle the changed properties.

public class Person 
{
   public string FirstName {get;set;}
   public string LastName {get;set;}
   public int Age {get;set;}
}

Let's say you notice the first name has a typo, Jhon and it is supposed to be John. You can edit the first name and send it up. So you have the following object model. You can get this in 1 of two ways:

  • Have two parameters and set BodyStyle = WebMessageBodyStyle.Wrapped
  • Just create a generic model object with two properties: Property one is of Type T and property 2 is a List.

So you would send up this json:

[{ FirstName = 'John' }, ['FirstName']]

Now on the server side, you can do what you need to do.

If you don't want to send the changed properties, you can guess the changed properties by choosing any property whose value isn't the default property.

{ FirstName = 'John' }

Then you can use a few methods to see what properties have changed:

Peppi answered 2/10, 2017 at 14:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.