Optimistic Concurrency in Model-Driven Apps Forms

This blog will discuss a proposed implementation of Optimized Concurrency for Model-Driven Apps forms. My implementation is executed on the model-driven app only, using the client API and the new Asynchronous capabilities that the API offers.

I won’t go into the details of my analysis that led to this implementation proposal. However, I was strongly inspired by two blogs, which caught my attention thanks to their level of detail and simple and efficient explanations:

DEMO:

How does it work?

As mentioned, the logic is executed on the client side only. In fact, the logic is as follows:

  • When the form is loaded. The row version is retrieved and stored in a global variable.
  • While saving the form, an asynchronous validation is performed. This consists in retrieving the actual record’s version from the server.
    • If this value is equal to the initial row version, then the save is not canceled.
    • Otherwise, the save will be canceled, and an error message will be displayed.
  • After the form has been successfully saved. The NEW row version is stored in the global variable.

Below is the code used for this implementation. Simply associate the OptimisticConcurrencyForm.OnLoad function with the OnLaod event on a form.

//Optimistic Concurrency in Model-Driven Apps Forms
var MEA = window.MEA || {};
var OptimisticConcurrencyForm = MEA.OptimisticConcurrencyForm || {};
(function () {
var versionNumber = null;
var entityReference = null;
this.OnLoad = async function (executionContext) {
var eventArgs = executionContext.getEventArgs();
if (eventArgs.getDataLoadState() == 1) {
var formContext = executionContext.getFormContext();
formContext.ui.addOnLoad(this.getVersionNumberOnLoad);
formContext.data.entity.addOnSave(this.validateOnSave);
formContext.data.entity.addOnPostSave(this.updateVersionNumberOnPostSave);
}
};
this.getVersionNumberOnLoad = async function (loadCtx) {
var formContext = loadCtx.getFormContext();
if (formContext.ui.getFormType() == 2) {
entityReference = formContext.data.entity.getEntityReference();
var onLoadResult = await Xrm.WebApi.retrieveRecord(entityReference.entityType, entityReference.id, "?$select=versionnumber");
versionNumber = onLoadResult["versionnumber"];
}
};
this.validateOnSave = async function (saveCtx) {
var validateRowVersion = (saveCtx, entityReference, versionNumber) => {
return (async () => {
var result = await Xrm.WebApi.retrieveRecord(entityReference.entityType, entityReference.id, "?$select=versionnumber");
var versionNumberFromServer = result["versionnumber"];
if (versionNumberFromServer == versionNumber) {
return true;
}
else {
saveCtx.getEventArgs().preventDefault();
return false;
}
})();
};
var formContext = saveCtx.getFormContext();
if (formContext.ui.getFormType() == 2) {
try {
var validationResult = await validateRowVersion(saveCtx, entityReference, versionNumber);
if (!validationResult) {
var errorOptions = { message: 'This record is already updated by another user! ' };
Xrm.Navigation.openErrorDialog(errorOptions);
}
}
catch (error) {
console.log(error);
}
}
};
this.updateVersionNumberOnPostSave = async function (postSaveCtx) {
if (postSaveCtx.getEventArgs().getIsSaveSuccess()) {
entityReference = postSaveCtx.getEventArgs().getEntityReference();
result = await Xrm.WebApi.retrieveRecord(entityReference.entityType, entityReference.id, "?$select=versionnumber");
versionNumber = result["versionnumber"];
}
}
}).call(OptimisticConcurrencyForm);

Hope it helps …

One thought on “Optimistic Concurrency in Model-Driven Apps Forms

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s