web
You’re offline. This is a read only version of the page.
close
Skip to main content

Announcements

No record found.

News and Announcements icon
Community site session details

Community site session details

Session Id :
Dynamics 365 Community / Blogs / XrmTricks / Optimistic Concurrency in M...

Optimistic Concurrency in Model-Driven Apps Forms

meelamri Profile Picture meelamri 13,218 User Group Leader

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.

This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Show hidden characters
//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 …


This was originally posted here.

Comments

*This post is locked for comments