I am looking for a solution to handle concurrent editing of data in a grid. The web application I am working on will run on multiple computers, where more than one person are able to edit the same record in the database.
It is an ASP MVC application, using wrappers for igGrid and other Ignite UI components (19.1).
The grid uses updating in cell mode and a save changes action method in my controller based on your development guide and API. The grid data is use a viewmodel object, not the actual database object.
Now, when save changes is fired, how can I verify that the object I am editing is based on the same version of that object in the database? As far as I have seen, only edited fields are sent in the transaction, so there is no way to validate the other fields of that object as these are not available. And since the action method gets the object from the database for each field it needs to update, as in your code example, it will get the most recent version, which may have been changed by another user while the current user was editing it.
So what are my options in this case?
Can I include fields (such as all editable fields or one specific hidden field (timestamp)) not edited in the save changes action from the client to the server? Is there any way to extend or modify the igGrid.("saveChanges") function in order to make this possible?
In my controller action method I will then need to determine if the data I am updating is based on the current database data or not.
Do I need to go for a different soultion?
Best regards
Fredrik Walka
Hello Fredrik,
Thank you for posting in our forum.
You can directly modify the transactions before they get send to the server and add the additional information that you may need in order to determine if the record has been modified.
The transactions can be retrieved via the allTransactions grid api method and you can modify them before calling saveChanges.
For example:
var transactions = $('#grid').igGrid('allTransactions'); for(var i = 0; i < transactions.length; i++) { transactions[i].additionalValue = 'test'; } $('#grid').igGrid('saveChanges');
Let me know if that would solve your issue.
Regards,
Maya Kirova
Thanks for the information. I think that would be a good way to include the actual rowversion.
I have tried my solution in a different way, which seems to suite my solution better. I have made a new test project using REST instead. In this case I have found it easier to handle errors, such as the concurrency error, but it is not working all the way. So with using this variant instead, I have a few questions:
When saving an updated row with success, I return
Request.CreateResponse(HttpStatusCode.OK, updatedModel);
with my PUT action in the API.My problem is that the data in the grid is not updated. The editable fields are, but not my rowversion, which means that if I edit the same row again without refreshing the page, I will get a concurrency error due to different rowversion values between the grids row data and the data I load from the database to update. I thought the returned data would be used to update the grid, but this does not seem to be the case.
I tried to put grid.igGrid("dataBind") when I have success in updating, but this returns a 404, trying to GET an url such as "http://localhost:57786/Home/api/units/?pk=Id&_=1562318767427". In the grid, I use .DataSourceUrl(Url.Action("/api/units/")). If this must be used, what do I need to implement? The api is in its owm controller and the page displaying the grid is in the homecontroller.
I am using routing in both home and api controller like:
[Route("unit/{id}/{date?}")] public ActionResult UnitIndex(string id, DateTime? date)
Is it possible this can be a problem?
Next, I am wondering if it is possible to use a asp mvc validation summary and return modelstate errors if any arise when updating. I have managed to display them for now, by extracting the actual messages and return them as a string array with the response, but using a validation summary would be better way. Is this possible?
The grid will not update automatically with the row data you have passed as part of the response.
The saveChanges method accepts two callback functions – one for success and one for error. So for the success function you can get the data that was returned form the server and update your local rowversion field for the related record using the grid api methods – updateRow, setCellValue. Note that those methods will generate new transactions in the log so you may need to clear the transaction after updating your local data. Using the callback function will be more performant than re-binding the whole grid, since a rebind will request the whole grid data and re-render it again.
In case the updating was not successful ( as a result of concurrency issues or model state not being valid) you can return an error response with the related message and handle the response on the error callback function of the saveChanges method (show error message etc.).
Let me know if you have any additional questions or concerns.
Hi again!
I modified the save function to handle the updated data and it seems to work as intended now. In my case, I only need to update the RowVersion since all other updated cells are correct. The code is as follows, which may help others in similar situation. This also include sending an error message when something goes wrong when saving on the server side, using the response.responseText.
$("#saveChanges").on('igbuttonclick', function (e) { grid.igGrid("saveChanges", function (data) { grid.igGridUpdating("setCellValue", data.Id, "RowVersion", data.RowVersion); }, function (response) { var res = JSON.parse(response.responseText); alert(res); }); loadingIndicator.hide(); $(this).igButton("disable"); return false; } );
On server side I return either
return Request.CreateResponse(HttpStatusCode.OK, updatedModel);
or on error
return Request.CreateResponse(HttpStatusCode.BadRequest, ModelState.Values.Where(v => v.Errors.Count > 0).SelectMany(e => e.Errors.Select(m => m.ErrorMessage)).ToList());