Advertisements

Archive

Posts Tagged ‘Optimistic Concurrency’

D365 Integration – Handling concurrent transactions

October 26, 2019 Leave a comment

In one of our integration requirements, we got to make sure that there wont be any concurrent updates/deletion to the records, to avoid data loss.

As an example, lets assume a Record 1 was retrieved by users U1 and U2 and both want to update ‘Name’ field same time, in this scenario it could end up with either U1 or U2 overriding each others data.

In such scenarios we can prevent the data loss using Optimistic Concurrency.

Whats an ‘Optimistic Concurrency’?

  • This feature provides the ability for your applications to detect whether an entity record has changed on the server in the time between when your application retrieved the record and when it tries to update or delete that record.
  • If you notice, every entity will have an OOB field ‘VersionNumber’ of type ‘Time Stamp’.
  • ‘VersionNumber’ value gets updated by system, every time when the record updates.

Concurrency_4

  • In below screen, my record ‘Transaction’ has the ‘Name’ set to ‘Payment to UBER‘.

Concurrency_5

  • Let me retrieve the record and before triggering update, let me update the ‘Name’ field to ‘Payment to UBER_Updated‘.
  • Now the Update fails with error code.

Concurrency_3

  • The update would go through, if there wont be any updates post retrieve.

C# Code snippet:

var queryTransaction = new QueryExpression(“raj_transaction”){
ColumnSet = new ColumnSet(“versionnumber”, “raj_name”)
};

var transactions = _service.RetrieveMultiple(queryTransaction);

foreach (var transaction in transactions.Entities){
// transaction.RowVersion

// Update Name
transaction[“raj_name”] = DateTime.Now.ToString();

//Update the account
var request = new UpdateRequest()
{
Target = transaction,
ConcurrencyBehavior = ConcurrencyBehavior.IfRowVersionMatches
};

try{
_service.Execute(request);
}
catch (FaultException<OrganizationServiceFault> ex){
switch (ex.Detail.ErrorCode){
case -2147088254: // ConcurrencyVersionMismatch
case -2147088253: // OptimisticConcurrencyNotEnabled
throw new InvalidOperationException(ex.Detail.Message);
case -2147088243: // ConcurrencyVersionNotProvided
throw new ArgumentNullException(ex.Detail.Message);
default:
throw ex;
}
}
}

Notes:

  • Optimistic concurrency is supported on all out-of-box entities enabled for offline sync and all custom entities.
  • You can determine if an entity supports optimistic concurrency by retrieving the entity’s metadata using code or by viewing the metadata using the Metadata Browser IsOptimisticConcurrencyEnabled is set to true.
  • Refer my older article on Optimistic concurrency using WEB API in client script.
  • Refer article for more details.

🙂

Advertisements
Categories: CRM Tags:

CRM 2016 Web API – Optimistic Concurrency

December 31, 2015 1 comment

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
  • eTag

    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.
Optimistic Concurrency

Optimistic Concurrency

  • If the value had matched, a 204 status is expected.

🙂