In this blog, CRM development services providers are explaining the way to manage concurrency in Dynamics CRM by taking Autonumber generation reference. You can read this post and find what they are following to make this happen.

Dynamics CRM/365 is multi-user application that supports threading. When there is high volume of read/update/delete actions, there might be new set of problems associated with data loss.

Think about these scenarios.

  1. A record is updated by CRM user when it is being edited by another user.
  2. A record is deleted by CRM user when it is being edited by another user.

There is decent probability of such scenarios when there are many concurrent users working on the records in parallel. Dynamics CRM 2015 Update 1 introduces a feature called Optimistic Concurrency to handle such scenarios during coding. This feature allows developers to keep track of versions of updates happened on a particular record and to detect possible concurrent updates.

Case study: Auto Number for Case Entity

  • Let us go through Optimistic Concurrency by taking Autonumber generation which is a perfect candidate to demonstrate this feature; for example, think about scenario when multiple records are being created by large pool of users.
  • As we know, Dynamics CRM/365 application does not support custom auto number functionality, we need to come up with custom code by means of a plugin which has to be called whenever there is new record inserted into database.
  • There are many ways of implementing auto number but, for this example, we will track latest autonumber by means of a configuration entity. To start with, the configuration entity has a record that has initial number assigned to start with, and this number is subsequently incremented for every record creation.
  • Hence, Create a custom entity with Key and Value attributes. Create a record with key name CaseAutoNumber and Value can be initial number, say 10000.
  • Plugin reads 10000 and assigns to a case record before it increments Config item to 10001 by leveraging optimistic concurrency feature.

Enabling Optimistic Concurrency

  • As per Microsoft article https://msdn.microsoft.com/en-us/library/dn932125(v=crm.8).aspx , Optimistic concurrency is supported on all out-of-box entities enabled for offline sync and all custom entities. There is IsOptimisticConcurrencyEnabled property in entity metadata that can be used to check whether an entity supports Optimistic Concurrency or not.
  • By default, all custom entities support this feature.

Handling Concurrency

  • There is RowVersion property of Entity class that can be accessed to check current version of a record. When the record is being updated or deleted, RowVersion can be set to the same value and Concurrency Behavior is set to IfRowVersionMatches, then platform throws exception if there is any mismatch. Otherwise, update or delete operation will happen successfully.
  • We always need to set RowVersion during Update or Delete requests. Otherwise, platform throws exception.

Understanding Concurrency Behavior

The messages such as UpdateRequest and DeleteRequest have ConcurrencyBehavior property which can be set by the user. According to https://msdn.microsoft.com/en-us/library/microsoft.xrm.sdk.concurrencybehavior.aspx, Concurrency Behavior is enum property that has following values

  • AlwaysOverwrite(Value = 2): This is default behavior when concurrency is not enabled for target entity. Records will be overwritten without any regard to its version.
  • IfRowVersionMatches(Value = 1): Specifies the behavior where an update or delete operation is only applied if the entity record in the database has the same row version as the entity or entity reference in the request.
// Code starts here

// Plugin for Case entity on create message and on pre stage event

if (context.InputParameters.Contains("Target") &&

               context.InputParameters["Target"] is Entity)

            {

                Entity incident = (Entity)context.InputParameters["Target"];

                // Pull config records from CRM

                // Key name is CaseAutoNumber

                string query = @"<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'>

  <entity name='crm_config'>

    <attribute name='crm_key' />

    <attribute name='crm_value' />

    <filter type='and'>

      <condition attribute='statecode' operator='eq' value='0' />

      <condition attribute='crm_key' operator='eq' value='CaseAutoNumber' />

    </filter>

  </entity>

</fetch>";

                EntityCollection collection = service.RetrieveMultiple(new FetchExpression(query));

                if (collection.Entities.Count > 0)

                {

                    Entity config = collection.Entities.FirstOrDefault();

                    int value = Convert.ToInt32(config.Attributes["crm_value"].ToString());

                    //update case record

                    incident.Attributes.Add("crm_casenumber", value.ToString());

                    try

                    {

                        // update config

                        config.Attributes["crm_value"] = (value + 1).ToString();

                        UpdateRequest updateRequest = new UpdateRequest();
 


                        // We are updating only if row version matches. Otherwise, we need to handle exception

                        updateRequest.ConcurrencyBehavior = ConcurrencyBehavior.IfRowVersionMatches;

                        updateRequest.Target = config;

                        updateRequest.Target.RowVersion = rowVersion;

                        service.Execute(updateRequest);

                    }

                    catch (FaultException<OrganizationServiceFault> ex)

                    {

                        //Faultcode for ConcurrencyVersionMismatch, it means that record has been changed by someone after record is retrieved and before the update action happens.

                        if (ex.Code.Name == "ConcurrencyVersionMismatch")

                        {

                            throw new InvalidPluginExecutionException("Create record again");

                        }

                    }

          }

}

//Code ends here

Conclusion:

Hence, it is always a good practice to use optimistic concurrency to prevent data loss especially when data is very important. Also, enabling auditing would help to keep track of changes happened for that record.

This is a case study shared by CRM development services providers to help community people. They have discussed the best way to manage concurrency in Dynamics CRM by taking Autonumber generation reference. If you have any question, ask in comments at the end of this post.