Archive
CRM 2016 Web API – Optimistic Concurrency
Assume a scenario, you retrieved an Account and wanted to update the ‘Name’ field. But due to concurrency the same Account has changed on the server since you retrieved it and you may not want to complete the update.
So how to detect this situation and avoid concurrency issues? The answer is ‘Optimistic Concurrency’ patterns provided by Web API.
What’s Optimistic Concurrency
- Optimistic concurrency can be used to detect whether an entity has been modified since it was last retrieved by using ‘ETag’ and ‘If-Match Header’.
- Etag:
- Each time when we retrieve an entity it will include a @odata.etag
-
- The value of this property is updated each time the entity is updated.
- Refer this article how to fetch Etag.
- If-Match Header
- If-Match header with the ETag value can be used to check whether the current value on the server matches the one found when the user last retrieved the record.
Sample Script:
Key Points to perform Optimistic Concurrency:
- Get the @odata.etag property of record up on retrieval.
- ‘Status’ code would be 412, if the current record is different from the server.
function updateRecord() {
var clientURL = Xrm.Page.context.getClientUrl();
var accountId = “f26b5f92-5798-e511-80e3-3863bb2ead80”;
var req = new XMLHttpRequest()
req.open(“PATCH”, encodeURI(clientURL + “/api/data/v8.0/accounts(” + accountId + “)”), true);
req.setRequestHeader(“If-Match”, “W/\”632353\””);
req.setRequestHeader(“Accept”, “application/json”);
req.setRequestHeader(“Content-Type”, “application/json; charset=utf-8”);
req.setRequestHeader(“OData-MaxVersion”, “4.0”);
req.setRequestHeader(“OData-Version”, “4.0”);req.onreadystatechange = function () {
if (this.readyState == 4) {
req.onreadystatechange = null;
if (this.status == 204) {
var data = JSON.parse(this.response, dateReviver);
}
else if (this.status == 412) {
var error = JSON.parse(this.response).error;
alert(“Precondition Failed – ” + error.message);
}
else {
var error = JSON.parse(this.response).error;
alert(“Error updating Account – ” + error.message);
}
}
};// Set Account record properties
req.send(JSON.stringify({ name: “Rajeev Pentyala” }));
}function dateReviver(key, value) {
var a;
if (typeof value === ‘string’) {
a = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
if (a) {
return new Date(Date.UTC(+a[1], +a[2] – 1, +a[3], +a[4], +a[5], +a[6]));
}
}
return value;
};
Server Response
- Web API call fails with 412 code, if the Etag value sent with the If-Match header is different from the current value in server.
- If the value had matched, a 204 status is expected.
🙂
CRM 2016 Web API – Retrieve a record
Below is the sample script to retrieve an Account record using Web API.
Key Points to perform Retrieve:
- Use ‘GET’ request while performing Retrieve
- ‘Status’ code would be 200.
Sample Script:
function retrieveAccounts() {
var clientURL = Xrm.Page.context.getClientUrl();
var req = new XMLHttpRequest()
req.open(“GET”, encodeURI(clientURL + “/api/data/v8.0/accounts?$select=name&$top=1”), true);
req.setRequestHeader(“Accept”, “application/json”);
req.setRequestHeader(“Content-Type”, “application/json; charset=utf-8”);
req.setRequestHeader(“OData-MaxVersion”, “4.0”);
req.setRequestHeader(“OData-Version”, “4.0”);req.onreadystatechange = function () {
if (this.readyState == 4) {
req.onreadystatechange = null;
if (this.status == 200) {
var data = JSON.parse(this.response, dateReviver);
if (data && data.value) {
for (var indxAccounts = 0; indxAccounts < data.value.length; indxAccounts++) {
var accountName = data.value[indxAccounts].name;
var eTag = data.value[indxAccounts][‘@odata.etag’];
}
}
}
else {
var error = JSON.parse(this.response).error;
alert(“Error retrieving Accounts – ” + error.message);
}
}
};req.send(null);
}function dateReviver(key, value) {
var a;
if (typeof value === ‘string’) {
a = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
if (a) {
return new Date(Date.UTC(+a[1], +a[2] – 1, +a[3], +a[4], +a[5], +a[6]));
}
}
return value;
};
ETag:
- Each time when we retrieve a record, it will include a @odata.etag field.
- We don’t need to include it in a $select system query option.
- The value of this property is updated each time the entity is updated.
- This will be used while performing optimistic concurrency to detect whether an entity has been modified since it was last retrieved.
🙂