[Tutorial & Sample] Client Property Tracking for PATCH


In OData Client for .NET 6.2.0, we enabled the top level property tracking on client side when sending a PATCH. This feature allows client to send only updated properties, instead of the entire object to server.

The top level property tracking means the client will only track the highest level properties in the class hierarchy. In other words, if customer is updating a property under a complex type property, or an item in a collection property under an entity, the client will send the entire complex type property (including unchanged properties under the complex property) or the entire collection (including unchanged items in the collection) to the server.

In implementing property tracking, we start by creating a DataServiceCollection instance. DataServiceCollection helps to track all the properties. For more information about DataServiceCollection, See “Binding Data to Controls (WCF Data Services)”.

Let’s move to a real example to explain how to do it.

Service EDM model


Following sample code will base on this model.

Client Code

You can read “How to use OData Client Code Generator to generate client-side proxy class” for generating client-side proxy class.

Now, we will show you how to enable property tracking when doing a PATCH.

1. We need a DataServiceCollection which will handle property tracking.

DataServiceCollection<Product> products = new DataServiceCollection<Product>(dataServiceContext.Products.Where(p => p.ID == 0));

2. Update the property

products[0].Price = 3.0;

3. Save changes

Http-Method will be “PATCH” when updating with SaveChangesOptions.None. This step will send the updated top level properties.




Sample Code

Here I will provide several samples with their payloads.

1. Update top-level primitive property. Only the updated primitive property will be sent.

Sample Code:



2. Update property in top-level complex property. The entire complex property (“Address” in the example) instead of the updated property (“City” in the example) in the complex property will be sent to server.

Sample Code:

Payload for complex type:


3. Update a navigation entity, only the updated property of the entity will be sent out.

Sample code:



If you don’t use DataServiceCollection, then client will not support property tracking. For example:

Although client tracked the entity after ToList() was called. SaveChanges() will not send any request. The client cannot track the property change.

If you tried to use following code to update the entity, then the whole entity will be sent, instead of the only updated properties.

Payload will be like

Comments (3)

  1. Uffe says:

    About time – this have been on top of my wish list for a long long time – finally, thank you. Any chance you will port this to V3? 😉 Currently I have my own implementation for this for V3, so I'll be fine. However I have one special capability that I would like you to add…

    As the property tracking is not in effect at all times (like in your ToList()/UpdateObject example above) we have in our custom OData provider a convention that we will ignore properties with null values during updates as we have no way of knowing if the submitted null value is due to the property not being part of of the initial $select from the client or if the client/user actually changed the value to null. Now should our server update the value to null or not? – we don't know, we have no way of telling if this is the intention. Just blindly updating with the null value would lead to data loss in cases where it was not part of the initial select/projection.

    To allow clients to actually set the server values to null we have special values denoting that intention, for DateTime types it would be DateTime(0, UTC).

    However this makes a rather odd coding experience on the client as this is not logical when you actually want to null a nullable DateTime property, but this is better than loosing random data!!!

    In my current V3 implementation of property change tracking I have a solution for this as I with the property tracker on the client can detect when the user/client actually changes a value to null and then in my PropertyTracker I can automatically patch it with the value that the server will accept as meaning null (like DateTime(0, UTC) or String("")). This solves the problem of the odd programming experience.

    In my implementation I have it as a method you could overload and provide your own patching values for whatever you like and it only gets called for properties that the user/client/programmer have actually changed.

    Off cause I do not expect you to implement our "magic" value behavior, but it would be really nice with the option to supply a value patcher that get called during request building.

    Does it make sense? Or are we the only ones facing such issues? Other solutions to the issue of the server still not being able to trust that null means null?

    Does the OData Client property tracking also work during creates/POSTs to minimize the payload to only include properties the client have actually set to something? (my implementation does)


  2. laylaliu says:

    @Uffe, thank you for your feedback.

    About property tracking for POST, we have plan to support it in later OData V4 Client.

    About null-property scenario, I am trying to understand your question. Do you mean that if user firstly "select/projection" an entity without several properties. And then client calls UpdatedObject() to update this entity. The server side doesn't know if client doesn't collect those properties or client intends to set those properties to null, right?

    If you use DataServiceCollection, this issue will not be a problem. Because only changed properties will show in payload. However, if you use UpdateObject(), you need to keep your old workaround. We will consider to provide better property tracking story in future.

  3. Uffe says:

    Hi Layla,

    Yes you understood me correctly. Yes I will need to keep my old workaround in my server (magic values representing null during updates) as the server does not know how the client was implemented (UpdateObject or DataServiceCollection or something else).