Concurrency has always been an issue with Dynamics CRM. In this post I will demonstrate how to create an optimistic concurrency control “feature” in a generic reusable way using JavaScript without adding any new attributes.
History
Almost every CRM customer that I dealt with asked at some stage whether it is possible to have a concurrency control mechanism in Microsoft Dynamics CRM. Customer complain that if two people are working on the same record and both save their work the last person who saved “wins”. Microsoft Dynamics CRM will only take the changes made to the last record.
In the following solution I will use a JavaScript script library to create concurrency control in CRM 2011. The JavaScript will execute twice. The first time on the load event it will register the current date and time. The second time on save it will check if we are online and call the CRM 2011 REST services to check if the ModifiedOn field has been updated past the time we register on load.
The JavaScript file looks like the following
var lastModifiedOn; function GetLastModifiedOn() { lastModifiedOn = new Date(); } function CanISave(executionObj, entityType) { if (Xrm.Page.context.isOutlookOnline()) { var request = Xrm.Page.context.getServerUrl() + "/XRMServices/2011/OrganizationData.svc/" + entityType + "Set(guid'" + Xrm.Page.data.entity.getId() + "')?$select=ModifiedOn,ModifiedBy"; var retrieveRequest = new XMLHttpRequest(); retrieveRequest.open("GET", request, false); retrieveRequest.send(); var resultXml = retrieveRequest.responseXML; var doc = new ActiveXObject("MSXML2.DOMDocument"); doc.async = false; doc.loadXML(retrieveRequest.responseText); var whoModifiedIt = doc.selectSingleNode('//d:Name').text var whenWasItModified = TimeStampToDate(doc.selectSingleNode('//d:ModifiedOn').text); if (whenWasItModified.getTime() > (lastModifiedOn.getTime() + lastModifiedOn.getTimezoneOffset() * 60000)) { var confirmAnswer = confirm("The record was last modified by " + whoModifiedIt + "nAre you sure you want to save it?"); //+ " on " + whenWasItModified.toLocaleString() + if (!confirmAnswer) { executionObj.getEventArgs().preventDefault(); } } } } function TimeStampToDate(xmlDate) { var dt = new Date(); var dtS = xmlDate.slice(xmlDate.indexOf('T') + 1, xmlDate.indexOf('Z')) var TimeArray = dtS.split(":"); dt.setUTCHours(TimeArray[0], TimeArray[1], TimeArray[2]); dtS = xmlDate.slice(0, xmlDate.indexOf('T')) var DateArray = dtS.split("-"); dt.setUTCFullYear(DateArray[0], DateArray[1], DateArray[2]); return new Date(DateArray[0], DateArray[1] -1, DateArray[2], TimeArray[0], TimeArray[1], TimeArray[2]); }
Implementation
In order to load this customisation create a new web resource of type Script (JScript) and save the JavaScript above in this newly created resource.
Once you have created the JavaScript. Go to any of the entity that you want and customize it by loading the appropriate form that you want to customize, adding the JavaScript library and finally add calling the correct methods on the correct events.
Go to the OnSave events to call the CanISave method. Don’t forget to pass the execution context as the first parameter and the schema name of the entity as the second parameter. Keep in mind that the schema name is case sensitive (Account, new_customentity….).
To test it open a record from the view that you have created, press CTRL+N to open the same record twice.
Change a filed in the newly created record and save and close this last one. Back on the first record, update a field and try to save it. You’ll be asked if you are sure you want to save this record.
Fredy Tandiary
Hi Rami, I noticed that you also have present a session at Dynamics User Group about “Implementing optimistic concurrency locking in CRM 2011 using RESTful Web Services”. Can you share the presentation and some code samples of this sessions?