ian.blair@softstuff

My technical musings

Preventing infinite loops in CRM2013/2015/2016 plugins

Imagine the scenario where you have a plugin that executes when an entity is updated. We will call this entity A, and the plugin updates entity B. Not usually a problem, but to make things more interesting we have a plugin on entity B that fires an update back to entity A. This then tries to execute the plugin again and this updates B which updates A again and it causes the plugins to fail with an infinite loop.

Good system design can often get around this and 99% of the time you wont have to worry about it, but for the remaining 1% then IPluginExecutionContext.Depth property comes in very useful.

This property shows the number of times the plugin has been called during the current execution and if it is more than 8 (setting WorkflowSettings.MaxDepth can be changed) the execution fails as the system considers that an infinite loop has occurred.

So in the first example entity A is updated and the plugin executes (Depth=1), B is updated and the other plugin updates, and A is updated again. Our plugin fires again (Depth=2) and B is updated, the other plugin fires and updates A. Our plugin fires again (Depth=3) and so on.

public class SampleOnCreate : IPlugin
{
	public void Execute(IServiceProvider serviceProvider)
	{
		Microsoft.Xrm.Sdk.IPluginExecutionContext context = (Microsoft.Xrm.Sdk.IPluginExecutionContext)serviceProvider.GetService(typeof(Microsoft.Xrm.Sdk.IPluginExecutionContext));
		IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
		IOrganizationService _service = serviceFactory.CreateOrganizationService(context.UserId);
		if (context.Depth > 1) { return; } // only fire once
		// do stuff here
	}
}

 

For most instances if you exit the plugin on context.Depth>1 will stop it running more than once from the main calling entity, and if you want it to be executed by updating entity A which then calls entity B then checking for context.Depth>2 will work, although of course actual code will depend on your requirements.