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

Community site session details

Session Id :
Microsoft Dynamics CRM (Archived)

Asynchronous execution of a pre-operation plugin

(0) ShareShare
ReportReport
Posted on by

Hello guys,
I have a few plugins which is running on the change of status of quote entity. One of the plugins is responsible for creating service activities on certain resources on the sites. This plugin is having the following properties:
Event Pipeline Stage: Pre-operation
Execution mode : Synchronous
Deployment : Server
Execution Order : 5

The problem is when two users are trying to activate the contract at the EXACT same moment , the service activities is overbooking on the resources(That is, the resources are getting double booked).

Now the problem I believe is that the plugin is running synchronously. How can I make it asyncrhonous?(Pipleine needs to be pre-operation and this prevents it from execution mode to be asynchronous). Or is there any other solution to make this work? That is only one instance of the
servioe activity creation needs to be executed at a time?

*This post is locked for comments

I have the same question (0)
  • Suggested answer
    Shidin Haridas Profile Picture
    3,497 on at
    RE: Asynchronous execution of a pre-operation plugin

    Hi Donald,

    You cannot have PreOperation or PreValidation plugin steps running asynchronously.

    The entire idea here is that before the Main operation (such as Create, Read, Activate), something has to be done, then it is a synchronous step.

    Now, your problem seems more of an optimistic concurrency control issue than the plugin issue.

    You might want to look at some other implementation methods, which check for resources created etc before proceeding.

  • Community Member Profile Picture
    on at
    RE: Asynchronous execution of a pre-operation plugin

    Hi Shidin,

    Thanks for the response. The code does that- It checks if the resource is available . Since two instances are getting executed at the EXACT

    same moment, the resources are showing as available for botth instances.

    It is basically a race condition. Is there a way to resolve it for a plugin?

  • David Jennaway Profile Picture
    14,065 on at
    RE: Asynchronous execution of a pre-operation plugin

    The general way to resolve race conditions is to place a lock to block other instance. There is a general pattern that allows you to lock a record within a plugin:

    1. Create a new Entity - let's call it lock_entity, and create a record for that entity
    2. Within your plugin, retrieve the lock_entity record, then update it. This will put an update lock on the record
    3. Then do your plugin actions -in this case read the resource availability, and book the resource
    4. Once the operation completes, the lock on step 2 will be released

    If another plugin instance starts, it will get blocked at step 2 when it tries to access the locked record.

    There are a couple of limitations to this approach:

    • It only works for stages within the plugin transaction
    • There's an annoying situation where it doesn't work. If the originating operation is initiate by an asynchronous workflow, CRM can end up executing multiple operations within the same transaction, so the locking doesn't work

    You could also try an optimistic concurrency approach, though you'll probably need a separate record on which to manage the row version, as your underlying issue is that you're not really having contention over a common record, but the absence of a record (i.e. the absence of a record indicating a resource is already booked)

  • Community Member Profile Picture
    on at
    RE: Asynchronous execution of a pre-operation plugin

    David,

    Thanks for the suggestion.

    The plugin  runs on the quote entity. So the plugin checks for resources and if available it will create "Service Activity"(Another entity) records.

    So two users are performing the action of clicking on a button called "Activate Quote" on two DIFFERENT quote records. Clicking on the button will trigger the plugin. Hence, your suggestion will not work. Even if I add an update statement on the quote record , it will not solve my issue. My actual issue is two service activities cannot be created at the exact same time on the same resource.

  • Suggested answer
    Shidin Haridas Profile Picture
    3,497 on at
    RE: Asynchronous execution of a pre-operation plugin

    Hey David,

    I think David's solution of having a separate lock entity might work in your case, assuming that the plugins are Synchronous.

    On click of 'Activate Quote' by User 1,
    1. Plugin triggers, retrieves the 'lock_entity' record and updates it. (D365 platform now puts a read lock on this record).
    2. Check for resources and allocate and create Service Activity.

    On click of 'Activate Quote' by User 2,
    1. Plugin triggers, retrieves the 'lock_entity' record. But because of the lock in the previous step, the plugin waits till the lock is released.
    2. After the wait time is over, when the plugin checks for resources, it sees that the resource is already allocated.

    This is how I would expect it to work, and would suggest you to try it out.

    If not, you would need to implement some sort of optimistic concurrency model, as I had initially suggested.
    https://msdn.microsoft.com/en-us/library/dn932125(v=crm.8).aspx

    Also, read through David's excellent post on the same here:
    https://community.dynamics.com/crm/b/crmdavidjennaway/archive/2018/03/29/concurrent-or-consistent-or-both

    Good luck!

  • Community Member Profile Picture
    on at
    RE: Asynchronous execution of a pre-operation plugin

    Thank you for your suggestion Shidin.

    Unfortunately it did not work.

    My code looks somewhat like this now:

     Guid oppRecToUpdate = new Guid("15E74CF6-3338-E811-80CC-005056AA8521");

     Entity oppRec = crmService.Retrieve("opportunity", eventRecToUpdate, new ColumnSet("def_test"));

    string notUsedForAnything = oppRec.GetAttributeValue<String>("def_test");

    oppRec["def_test"] = "Test";

    rc.DeleteAllPreviousServiceActivities(quoteRecord.GetAttributeValue<EntityReference>("opportunityid").Id);

    rc.ReserveResourcesOnQuotetActivation(quoteRecord, resourcesToReserve, allProductResources, tracingService);

    crmService.Update(oppRec);

    The DeleteAllPreviousServiceActivities function as the name implies, deletes all the service activities if any previously associated to the event. ReserveResourcesOnQuoteActivation is the main function which checks if the resources are available for the time needed. It later books the resources based on availability. Am I doing something wrong here?

  • Suggested answer
    Arpit Shrivastava Profile Picture
    7,518 User Group Leader on at
    RE: Asynchronous execution of a pre-operation plugin

    Hi Donald,

    There is one feature in Dynamics CRM Plugin called Optimistic Concurrency Control.

    Which is useful in the case, When more than one user selects the same record and tries to save the record with different updates at the same time (either through CRM Platform or Plugin code), To avoid this, you can enable optimistic concurrency when saving/updating/deleting the records. 

    How does it control the Concurrency?

    Answer - using RowVersion

    Each database has a counter that is incremented for each insert or update operation that is performed on a table that contains a rowversion column within the database. This counter is the database rowversion. This tracks a relative time within a database, not an actual time that can be associated with a clock. A table can have only one-rowversion column. Every time that a row with a rowversion column is modified or inserted, the incremented database row version value is inserted in the rowversion column.

    Understand through Image:

     

    Reference: https://community.dynamics.com/crm/b/tipsandtricksofcrm/archive/2015/08/09/optimistic-concurrency-in-dynamics-crm

    How can I use that in Plugin?

    Let say I want to update an account of Guid 345EFFB3-10WS-E511-80E2-C4346BAD87C8

    Entity accountRecord = _service.Retrieve("account", new Guid("345EFFB3-10WS-E511-80E2-C4346BAD87C8"), new ColumnSet(true));   

    //Code to Update the Record
    //Initialize new Object to update
     
      Entity updateRecord = new Entity();
     
      updateRecord.Id = accountRecord.Id;
     
      updateRecord.LogicalName = accountRecord.LogicalName;
     
      //Set the RowVersion Property of the Updating record
      updateRecord.RowVersion = accountRecord.RowVersion;
     
      //Update Name
      updateRecord["name"] = "Account Changed";
     
      UpdateRequest updateReq = new UpdateRequest();
     
      updateReq.Target = updateRecord;
     
    //Set the Concurrency Behaviour before executing the Update request & match the Row Versions of the record before executing the Request
    updateReq.ConcurrencyBehavior = ConcurrencyBehavior.IfRowVersionMatches;
     
      // Do the update.
      UpdateResponse updateResponse = (UpdateResponse)_service.Execute(updateReq);
     
    Now, suppose before UpdateRequest is executed the name of the record is changed in CRM at the same time by a different user.
    Then _service.Execute() will give an Exception: ‘The version of the existing record doesn't match the RowVersion property provided.’ and it would not be executed successfully.
    If you want to forcibly perform the Update operation through code then simply change this line as mentioned below:
    //Set the Concurrency Behaviour before executing the Update request to Ignore the check and update the record successfully
    updateReq.ConcurrencyBehavior = ConcurrencyBehavior.AlwaysOverwrite;
    You can take reference of following articles to incorporate in your plugin code.
    Hope it helps.


    If found useful, please mark the answer as verified.

     


    Cheers
    Arpit
    https://arpitmscrmhunt.blogspot.com

     

Under review

Thank you for your reply! To ensure a great experience for everyone, your content is awaiting approval by our Community Managers. Please check back later.

Helpful resources

Quick Links

Responsible AI policies

As AI tools become more common, we’re introducing a Responsible AI Use…

Pallavi Phade – Community Spotlight

We are honored to recognize Pallavi Phade as our Community Spotlight honoree for…

Leaderboard > 🔒一 Microsoft Dynamics CRM (Archived)

#1
Community Member Profile Picture

Community Member 2

#1
UllrSki Profile Picture

UllrSki 2

#3
SC-08081331-0 Profile Picture

SC-08081331-0 1

Last 30 days Overall leaderboard

Featured topics

Product updates

Dynamics 365 release plans