How to prevent infinite looping without ExecutionContext.CallerOrigin in Microsoft Dynamics CRM 2011?
Asked Answered
A

4

6

When creating a plugin in Microsoft Dynamics CRM 4.0 you could use the following to check the origin of the event that caused the plugin to fire.

public void Execute(IPluginExecutionContext context)
    {
        if (context.CallerOrigin.GetType() == CallerOrigin.WebServiceApi.GetType())
        {
            return;
        }
        plugin code here...
     }

This would allow you to check if the action was caused by a user in a form, by a web service or a workflow etc...

I have a syncing app that creates and updates entities via WCF, and do not want the plugin executing when this happens, only when users edit entities (to prevent infinite loops in the sync process).

IExecutionContext.CallerOrigin has been removed in MS Dynamics CRM 2011, so what is the new way to do this?

I was thinking that there may be a way to set IExecutionContext.CorrelationId in the WCF calls and then check for it the specific Guid in the plugin, but I haven't had any luck with this yet.

Abbate answered 16/5, 2011 at 7:27 Comment(0)
S
2

Have you looked inside the IPluginExecutionContext.InputParameters?

The other option would be to modify your plugin to not update anything if there were no changes, which would prevent the possibility of the infinite loop.

Soriano answered 17/5, 2011 at 16:31 Comment(2)
I might have to not update if the change was initiated by a specified user, as there will be changes by the sync tool. I'm really trying to prevent double syncing as opposed to infinite looping :) as I can stop the cycle in the sync toolAbbate
context.InitiatingUserId give you the guid id. In my case I want to exclude single user from firing plugin and this did the trick.Georgeanngeorgeanna
R
12

Although this seems to have been asked some time back (and I presume the OP has found his solution by now!) I came across it looking for a similar answer recently. It took further research to find out what I needed so for this reason I'll add it here too for anyone else that comes across it.

Firstly, if you are looking for it, this property has been made obsolete. Supposedly because it was unreliable, but there were a few reasons why we needed the CallerOrigin in MSCRM 4.0. On the other hand, there are ways around this becoming obsolete too:

Prevent infinite loops (over 2 plugins)

This was the reason I was looking for the CallerOrigin and how I came across this question. I only wanted the plugin to fire if it came from a user on the form, not from another plugin (i.e. asyc process/webservice). In my case the distinction of it being "over 2 plugins" is quite important, because I cannot use InputParameters to solve the problem. My example was similar to the following:

  • Update Plugin for "Parent" Entity. If optionset called "Status" on the parent entity was set to "Approved" I subsequently wanted to set a status on all the child entities to "Approved" as well.

  • Update Plugin for "Child" Entity. If the optionset called "Status" on the child entity was set to "approved", and all other children of the same parent has this set to "Approved" I needed to update the Status on the parent to approved as well.

This causes an infinite loop if you don't protect yourself against it. You also cannot use the InputParameters to solve it. One basic solution is to use depth checking:

context.PluginExecutionContext.Depth

If this is greater than 1 it has been called by another plugin/workflow. Note: If you have a workflow that is triggering the initial update you might want to be careful with what value you are checking for.

Prevent syncing issues from an offline client

We've been given different properties to help us distinguish these ones. Use these instead:

context.PluginExecutionContext.IsExecutingOffline
context.PluginExecutionContext.IsOfflinePlayback

Reacting differently depending on what the origin is

OK, so this is the only scenario where we really do need the CallerOrigin. The only way I think you'd be able to do this is by checking the type of the PluginExecutionContext itself. I know for async it's the type:

Microsoft.Crm.Asynchronous.AsyncExecutionContext

and for plugins it seems to be:

Microsoft.Crm.Extensibility.PipelineExecutionContext

Not sure what it is when coming from an external source, I unfortunately don't have any code available at the moment to test and figure this out. Outside of all that you would probably have to check:

PluginExecutionContext.ParentContext

The only other method I've come across for detecting where the update came from is using a custom flag on the form. So you could create an OptionSet called "OriginOfChange" (or something similar) with the options

  • CRM Form (JavaScript onsave)
  • Workflow
  • Plugin
  • etc.

Then what ever updates the entity sets this field during the update. In this way you could check the Input Parameters each time to see where the update has come from.

This last method is most likely the safest to employ if you need to react differently depending on the source.

Rework answered 13/3, 2012 at 16:16 Comment(3)
This is the best answer here, although you are technically incorrect when you assert that if the depth is greater than one, this plugin is being called by another plugin. If a workflow causes a plugin to fire, that would have a depth of 2, if a series of workflows chained together caused the plugin to fire, the depth would be equal to the number of workflows chained together + 1.Skardol
@JosephDuty Thanks for the feedback, excellent point. I will update the question to reflect this.Rework
To add to the last 2 points, I added a bit of extra detail around the plugin/workflow and depth checking. In hindsight (and a couple of more years experience!) I think you might need to be careful when just using depth checking for this purpose. The last method I suggested is the best (in my opinion) to use. I have left the crux of the original answer in place though as I don't think it should be edited too much.Rework
U
3

This thread's solution is to "Just check for context.depth property, if it's greater then 1 return"

It worked perfectly fine for my update plugin where I was updating the entity within it, causing the plugin to get fired twice, but on the second time, it checked for the depth and exited.

Update

By far the safest method is to use the shared variables rather than plugin depth though. If the only thing that is checked is the plugin depth, then anytime another plugin triggers another plugin, it won't execute because it's depth is 2, even though it is the first time the plugin has fired for the Update event.

Underplot answered 2/8, 2012 at 15:13 Comment(1)
I've always been under the impression that this becomes unreliable in the situation where you have, for example, a workflow or plugin updating your entity and triggering the code. E.g. if your plugin was designed to add new_fieldA to new_fieldB and populate new_fieldTotal and it should fire if new_fieldA or new_fieldB changes, then Depth at runtime via a UI change is 1. I thought that if another plugin or a workflow changes new_fieldA or new_fieldB thereby triggering your plugin then Depth > 1. I should probably test this, but I comment here in case someone else already has... :)Shatterproof
S
2

Have you looked inside the IPluginExecutionContext.InputParameters?

The other option would be to modify your plugin to not update anything if there were no changes, which would prevent the possibility of the infinite loop.

Soriano answered 17/5, 2011 at 16:31 Comment(2)
I might have to not update if the change was initiated by a specified user, as there will be changes by the sync tool. I'm really trying to prevent double syncing as opposed to infinite looping :) as I can stop the cycle in the sync toolAbbate
context.InitiatingUserId give you the guid id. In my case I want to exclude single user from firing plugin and this did the trick.Georgeanngeorgeanna
C
-3

The Depth is of execution, not recursion. You can recieve a Depth > 1 the first time your plugin got executed. Think of it as a Level in the execution pipeline (actually is the execution stack depth), some one got it first, when execution is passed the Depth is increased by 1, so the next in line do some other operation and before passing it again to the pipe increments +1 depth, now Dynamics executes your plugin, your depth would be 3 (Initial 1 [+1|+1]). CRM 2011 on-premise is limited by default to 8 and on-line is limited to a depth of 16.

So, recursion prevention by using the Depth you are just ASSUMING it, you can not assert it.

MSDN IExecutionContext.Depth Property

My 2 Cents, Best Regards Eric Arean

Carlottacarlovingian answered 5/11, 2013 at 14:41 Comment(1)
Although I agree that you're technically correct here, your answer is out of context in regard to the question asked. If this was a comment against a previous answer I might have upvoted instead.Rework

© 2022 - 2024 — McMap. All rights reserved.