Simple.Odata.Client - Odata Patch Entity by only sending modified properties to server

1.8k Views Asked by At

I'm using Simple Odata Client to perform CRUD operations in a WPF application.

I have a parent & a child entity:

public class Order
{
    public int OrderId{get;set;}
    public int Description{get;set;}
    public ObservableCollection<OrderLine> OrderLines {get;set;}
}
public class OrderLine
{
    public int OrderId{get;set;}
    public int OrderLineId{get;set;}
    public int ItenId{get;set;}
    public int ItemDescription{get;set;}
    public virtual Order Order {get;set;}
}

And I have a class for perform crud operation:

public class ManageOrders
{
    //Implements INotifyPropertyChanged
    Public Order Order;

    public void Get()
    {
        this.Order = packages = await client
                    .For<Order>()
                    .ByKey(1001).
                    .Expand(x.OrderLines).
                    .FindEntriesAsync();
    }

    public void Save()
    {
        if("NEW")
        {
            // Add new item and save
        }
        if("MODIFIED")
        {
            // save modified item
        }
    }

    public void Delete()
    {
        //Delete
    }   
}

enter image description here

I'm binding Parent entity properties to the header controls.

TextBox.Text = Order.Description;

and child entity to DataGrid.

DataGrid.ItemSource = Order.OrderLines;

When I click the GET Button, Order will be fetched from DB. Then I change the data in Order and OrderLines. Then I Delete a OrderLine and add two new OrderLines.

As I'm using ObservarbleCollection, the changes will be automatically added to Source from UI.

Requirement

When I click SAVE button all the changes should be submitted to the server. (Batch request is preferred).

Question

How can I send only changed entities to the server through a PATCH request, without sending unmodified properties both in header and lines?

1

There are 1 best solutions below

2
On

What you are asking for here is IMO the Holy Grail of web services and client-side frameworks.

Patch in OData makes it possible and simple to receive and process only changed properties for an object.

However it is up to the client to build the data packet appropriately, that can be done in 1 of two ways:

  1. Before sending, the client should compare the data to send against the data that was last retrieved to determine the properties that have changed.

  2. If the data on the client is wrapped in some kind of view model the view model could track (or watch) changes made to the properties.

Then on send, the client side must use this information to build a Delta of the object graph.

You haven't included any information on how you are generating your URIs to the backend service, so I don't want to guess but what ever the mechanism, 1 of the 2 strategies above will need to be implemented.

If your backend is an OData v4 service then you may find the OData Client package a helpful place to start. See OData Client library for .NET and the following to generate the client side object graphs: OData v4 Client Code Generator. You can use this against any service that implements the OData v4 specification, not only when you control the code for the backend.

Supports multiple modes for batching as batching is often implemented differently across OData v4 Implementations. For that reason I have left batching out of this discussion, but know that it is supported natively by these libraries and does work quite well.

For an example of usage of the generated classes see this SO question: What is the correct way to call patch from an odata client in web api 2 Or follow one of my unit tests:

    [TestMethod]
    public void TestPatch()
    {
        var client = ArcoCloud.Gateway.Client.Runtime.GetGatewayClient();
        var changeTracker = new Microsoft.OData.Client.DataServiceCollection<ArcoCloud.Gateway.Client.ArcoCloud_DataModel.Device>(client.Devices);

        // just change device 96
        var device = changeTracker.Single(d => d.Id == 96);
        device.Notes = "This is a test note to check if patch works natively";

        client.SaveChanges();
        /* Traced in Fiddler4
        PATCH: {
          "@odata.type": "#ArcoCloud_DataModel.Device",
          "Notes": "This is a test note to check if patch works natively"
        }*/
    }

Notice here to get the client to only send the modified properties are using a Microsoft.OData.Client.DataServiceCollection which inherits from ObservableCollection, but has the additional benefits of keeping an internal track of the changes to the objects in it's query. see DataServiceCollection Class.

If you do use the OData Client Library and the Generated classes you can easily query the data service without the DataServiceCollection, but if you do that, updates will PUT the entire object graph. You will also find the syntax to save changes back quite wordy and hard to use. This is by design, to write back you should use the DataServiceCollection. The framework offers the simply query mechanism so that you can simplify the processes in your apps, Querying/browsing/filtering data can be isolated from the code that a data edit window might use to load and save it's data.

That's the official MS way to interact with OData v4 services from C# code. The beauty of the T4 templates is that you can customize the template where needed or extend the generated partial classes so that your business logic does not get overridden when the classes are re-generated

You can roll your own mechanism to support this if you want, just remember the two options, either track the changes as they happen, or use comparison before save to identify which fields have changed.