1. Marco Meoni
  2. .NET DataStore
  3. Thursday, 3 March 2022 21:39 PM UTC

Hello,

Controller/Service scaffolding in SnapDevelop API generates HttpPost method that can also be used for HttpPut, i.e. for updating only one row of a given model (which is a must for non-PB REST client):

// Controller
[HttpPut]
public async Task<ActionResult<int>> UpdateAsync([FromBody]Employee employee)

//Service
public async Task<int> UpdateAsync(Employee employee,CancellationToken cancellationToken)
{
var dataStore = await this.RetrieveByKeyAsync(new object[] {employee.emp_id},cancellationToken);
dataStore.SetModel(0, employee);
return await dataStore.UpdateAsync(cancellationToken);
}

Now, cients can send JSON to http://localhost/api/employee/1 as PUT request and the DB update works fine, because .NET datastore SetModel() sets the data of Employee object to the first row of the DataStore:

{
emp_id: 1,
emp_lname: "Smith",
emp_fname: "John",
emp_deptid: 100,
[...]
}

Instead of PUT, (non-PB) clients better send a PATCH verb because it allows for partial updates:

{
emp_id: 1,
emp_deptid: 200
}

but with this body the Controller returns all the fields in the model object that are nulls except emp_deptid, which makes sense since it is saying I want a Models.Employee object.

Is there a way to detect which properties have actually been sent in the JSON request so I can update only those fields?
The "silly" solution is to test all properties in the JSON and keep old values in the TModel (e.g. Employee) if not sent by the client, but this requires per-model implementation.

Am I missing a .NET datastore method that does this trick?

Microsoft suggests to use JsonPatchDocument in the Patch action of a Controller, but this goes beyond the datastore API:

[FromBody]JsonPatchDocument<Employee> employee

Thanks,

.m

Logan Liu @Appeon Accepted Answer Pending Moderation
  1. Monday, 7 March 2022 13:20 PM UTC
  2. .NET DataStore
  3. # 1

Hi Marco,

I think you can use JsonPatchDocument together with .NET DataStore to partially update a row.

Please try to call dataStore.GetForUpdate(rowIndex) to get the reference of a model, then use a JsonPatchDocument<TModel> object to apply the data to the model.

        public async Task<int> UpdateAsync(int id, JsonPatchDocument<D_Product> productPatch, CancellationToken cancellationToken)
        {
            var dataStore = await this.RetrieveByKeyAsync(new object[] { id }, cancellationToken);
            
            // Get the reference of the model (first row)
            D_Product product = dataStore.GetForUpdate(0);
            
            // Modify values
            productPatch.ApplyTo(product);
            
            // Save changes to database
            return await dataStore.UpdateAsync(cancellationToken);
        }

For more details about JsonPatch in ASP.NET Core web API, refer to https://docs.microsoft.com/en-us/aspnet/core/web-api/jsonpatch?view=aspnetcore-3.1

Regards,

Logan

Comment
There are no comments made yet.
Marco Meoni Accepted Answer Pending Moderation
  1. Sunday, 13 March 2022 10:50 AM UTC
  2. .NET DataStore
  3. # 2
Hi Logan,
thanks to GetForUpdate(), and the addition of simple changes to the Controller, I can make the HTTP PATCH working.
However, the client needs now to send a JSON array of operations to apply to the resource, for example
 
[
{ "op": "add",
"path": "/emp_deptid",
"value": 200
},
....
]
 
instead of just the JSON data to patch:

{
emp_id: 1,
emp_deptid: 200
}
 
It works, but the extra-coding is now up to the client.
What's your opinion/experience?
 
Best,
.m
 
Comment
  1. Logan Liu @Appeon
  2. Tuesday, 15 March 2022 10:43 AM UTC
Hi Marco,

For the client side, you can find a library that supports JSON-Patch for many languages. Refer to: http://jsonpatch.com/#Libraries

For the server-side, there are some flexible solutions to know which properties have been changed according to the JSON data. E.g.:

1) Parse the HttpContext.Request.Body JSON string directly.

2) Use the IDataUnpacker (https://docs.appeon.com/snapobjects/3.1/api_reference/SnapObjects.Data/IDataUnpacker/IDataUnpacker.html) provided in SnapObjects as the parameter type of the Action in the Controller to help process JSON quickly.

3) Implements a new generic type to present a model entry with the state (similar to IModelEntry<T>: https://docs.appeon.com/snapobjects/3.1/api_reference/SnapObjects.Data/IModelEntry1/IModelEntry.html).

Then support receiving this custom type of parameter in your action method, refer to:

https://docs.microsoft.com/en-us/aspnet/core/mvc/advanced/custom-model-binding?view=aspnetcore-3.1

Regards, Logan

  1. Helpful
There are no comments made yet.
  • Page :
  • 1


There are no replies made for this question yet.
However, you are not allowed to reply to this question.