1.800.323.3639 | Support | Training

Avtex

Preventing Plugin Recursion

| No Comments

When writing plugins, it is essential to know when to prevent the plugin from executing. First, this will assist with saving resources. If your plugin doesn’t need to run, preventing its execution will prevent the resources from being used. Secondly, if the plugin is executed repeatedly, without any checks for recursion, you will receive an infinite loop error, and your save will fail. The SDK offers a number of checks and balances you can utilize in order to check for, and prevent, recursion.

Register Step on Pre-Operation Stage. There are three stages you can use when registering your plugin step: Pre-Validation triggers outside the CRM transaction and is usually used to check for data errors, as no data would be written to the CRM during this stage. Pre-Operation is triggered before the data is saved to the CRM, so you can edit and update your data before it is saved to the CRM. Post-Operation is triggered after the data has been saved to the CRM, so you can ensure you are receiving the latest data values after all other Pre-Operation stages have been completed. When updating data in the Pre-Operation stage, there is no additional Update message triggered, while the Post-Operation stage will trigger another update if any values are updated in your plugin.

MSDN: Event Execution Pipeline

Select Filtering Attributes. When registering your plugin on an Update message, you have the option to selet filtering attributes. If you don’t select any filtering attributes, your plugin will execute every time the entity is updated, whether it is updated in the CRM, by a workflow, by a plugin, or by an external source through the Web Services. By selecting specific fields in the filtering attributes, you are telling the plugin to only trigger if those specific attributes have been updated. Of course, one thing to be aware of when using filtering attributes is that your attribute will appear in the target entity. If your plugin calls an update to the target entity, it will update the attribute, even if its value has not changed, thus causing the plugin to trigger again, potentially triggering an infinite loop.

PluginRegistration.png

Filtering Attributes in Plugin. This is essentially the same thing as filtering attributes in the plugin registration process. The difference is that you are verifying that one or more of the fields exist in the target entity, and if they’re not found, the plugin executes gracefully. This would have the same limitation as the Filtering Attributes, in that updates to the target entity directly could cause the plugin to trigger again, risking another infinite loop error.

if (context.InputParameters.Contains("Target") &&
    context.InputParameters["Target"] is Entity)
{
    Entity entity = (Entity)context.InputParameters["Target"];
}

if (!entity.Contains("firstname") ||
    !entity.Contains("lastname"))
{
    return;
}

Compare PreEntityImage to PostEntityImage. This takes the previous step of filtering attributes to the next level. When registering your Update message in the plugin registration, you can specify a pre and post entity image to be loaded into the execution context. The pre image contains the values of the attributes before they were changed in the CRM, while the post image contains the values of the attributes after they were changed in the CRM. By comparing the attribute in the pre image, to the attribute in the post image, you can see which values have changed. If any of the attributes you want to filter on have changed, you can continue executing the plugin, otherwise, you can exit the plugin gracefully. Since you are storing the data from the entity before and after, this will cause additional overhead for the plugin execution. It will be a good idea to make sure you only add the attributes you want to compare to the images, to reduce the overhead as much as possible.

MSDN: IExecutionContext.PreEntityImages Property
MSDN: IExecutionContext.PostEntityImages Property

Don’t Update the Target of Post-Operation Stage. The target entity contains the values that were updated at the time the entity was saved. If you are running your plugin in the Pre-Operation stage, you will do all of your updates to the target entity. This will ensure that your updates will be committed to the CRM, and the Pre-Operation stage updates will not trigger an additional update message. On the otherhand, if you are running your plugin in the Post-Operation stage, you will not want to update the target entity. If you do, you will trigger an update using the same attributes, even if the attributes have not been changed. Instead, create a new entity with the appropriate id. You will add only values that have changed and call the update function on this entity only. This will ensure that only the changed attributes get updated in the CRM, and prevent triggering any workflows or plugins that filter by other attributes.

Entity updatedEntity = new Entity(context.PrimaryEntityName);
updatedEntity.Id = context.PrimaryEntityId;
updatedEntity["name"] = "New Name";
service.Update(updatedEntity);

Check Execution Depth. The execution context in the SDK has a property called Depth, which can be used to check what step the plugin is being triggered on. If you are saving the record in the CRM, the depth will usually be 1, and any subsequent call to the plugin would increase the depth by 1. Unfortunately, this isn’t a foolproof method, as the update could be triggered by a workflow or even another plugin. If that happens, then the depth could be 2, 3 or even 4 before the plugin is triggered for the first time. If you check for depth, and your number is too low, you run the risk of not having your plugin being triggered when necessary. If your number is too high, you could be calling your plugin multiple times, and using additional resources and execution times for the plugin to complete.

MSDN: IExecutionContext.Depth Property

Check CorrelationId. The CorrelationId is another parameter exposed by the execution context. This is a Guid that keeps track of the ID of the id of the plugin or workflow execution. This one is a little harder to validate against, but is an excellent way to keep track of your current execution process and prevent your plugin from triggerng multiple times in the same process. Store the CorrelationId in a threadsafe global variable in your plugin, and when it executes a second time, compare the value stored in the global variable with the one passed in the execution context. If the two match, you know you’re running in the same process, and you can execute the plugin gracefully.

MSDN: IExecutionContext.CorrelationId Property

As you can see, there are many different methods that can be used to ensure that you don’t run your plugin steps more times than is necessary. You can use one or more of these approaches, and be flexible. What works for one plugin, may not work the same for another plugin, depending on the business logic you’re trying to adhere to. Experiment and see what works best for the plugin you’re writing.

Let’s Talk About CX

X