Making use of transactions in Dynamics 365 Customer Engagement

I believe that if you have ever written a CRM plugin, you are aware of the event execution pipeline ( and that the operation is run in transaction which means that if there will be any exception, the whole transaction will be rolled back. Pre-operation plugins and Post-Operation plugins are also run in transaction, so if there will be an exception raised by the plugin, changes will not be reflected in CRM. This includes not only chaning attributes for the entity that the plugin is running on – create/update/delete changes on any other entities are also rolled back. This is really convenient – we can perform a bunch of operations and we are sure that even if anything goes wrong during Post-Operation, everything will be changed back to the original state.

The usage of transactions can be embraced even more by using custom Actions and ExecuteTransactionRequest. Custom  Actions can also be run with a rollback option (which can be enabled or disabled on Action configuration page). ExecuteTransactionRequest allows to group different OrganizationRequests and send them in bulk to the server – they will be run in single transaction.

Ok, but how this transactional behaviour works in real world? To make it easier to understand for you I created a custom entity “new_lock” and prepared a very simple CRM plugin:

public void Execute(IServiceProvider serviceProvider)
    var factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
    var service = factory.CreateOrganizationService(null);
    var @lock = new Entity("new_lock");
    @lock.Id = new Guid("590424F1-611F-E711-80D2-00155D010402");
    @lock["new_name"] = "lockme";
    Thread.Sleep(30 * 1000);

I registered this plugin on Create message of the Contact entity. As you can see it simply retrieves the “new_lock” record and updates its name to some different value and then waits 30 seconds. Let’s open this record in a browser:

Now, let’s create a Contact (using a different browser window). After clicking “Save” you will notice that the loader starts but the data is not getting saved. That’s ok because our plugin is running in the background and it takes about 30 seconds to finish. If you try to save the opened “new_lock” record with some different name, you will notice that… you can’t do this:

This is the principal idea of transactions – by creating a Contact we opened a transaction and “locked” the “new_lock” record by updating it. Before the transaction is finished nothing can write to this record. After 30 seconds, our contact is created and the “new_lock” is updated with the new value. Is this a value from the plugin or from what we have entered on the form? Well it’s the second option, because we updated the value after the transaction has done it (it does not matter that it waited 30 seconds after that).

How can we make use of that transactional behaviour of CRM? I believe that a very cool feature that we can develop is proper auto-numbering. It’s pretty well known problem which is usually handled by some custom counter entity. The algorithm usually works like that:

  1. Run plugin on create of numbered entity
  2. Retrieve Counter
  3. Assign current value of counter to entity record
  4. Update counter with the next value
  5. Plugin ends

If you don’t have many users or some custom apps creating entities, you will probably never run into any concurrency problems. But imagine what will happen if you have two concurrent calls and both retrieved value of the counter before point 3 was fulfiled. This means that both records will have the same number and counter will be increased only by 1. Some may say – ok let’s use standard locking, we can have a static object to lock on and before plugin starts it’s core logic, the running thread must obtain the lock. That would work flawlessly on single-frontend-server environments. But what happens if there are more application servers for CRM? You cannot synchronize threads between different servers…

Knowing what we already know about transactions, we can easily modify this scenario to:

  1. Run plugin on create of numbered entity
  2. Retrieve Counter
  3. Update some dummy counter property to some dummy value (here we will lock the counter for usage only for this transaction)
  4. Retrieve Counter (now we are sure nobody will modify its value)
  5. Assign current value of counter to entity record
  6. Update counter with the next value
  7. Plugin ends (and any concurrent operations start from point 3)

This should work even on multi-server environments, because the transactions are locked on database level. Very simplified code (so pardon the style) can look like this:

public class TransactionPlugin : IPlugin
    public void Execute(IServiceProvider serviceProvider)
        var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
        var factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
        var service = factory.CreateOrganizationService(null);

        var counter = GetCounter(service);
        counter["new_name"] = "lockme";

        //now counter is locked

        //we have to get it again to prevent race conditions
        counter = GetCounter(service);

        var target = (Entity)context.InputParameters["Target"];
        target["new_number"] = counter["new_nextnumber"];

        counter["new_nextnumber"] = (int)counter["new_nextnumber"] + 1;
        //after finishing the transaction, concurrent executes can go on from the lock

    private Entity GetCounter(IOrganizationService service)
        //return some counter


Author: pawelgradecki

I'm a Dynamics 365 solution architect, but I'm a also a huge fan of .NET, C# good programming patterns and habits. I worked in many CRM projects as a Consultant, Lead Developer and/or Architect. Many times I've dived deep into the guts of CRM, I'd like to share with you what I've learnt.

Leave a Reply

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

You are commenting using your 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 )

Google+ photo

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

Connecting to %s