Monday, September 16, 2024

Power Platform (MS CRM/CE) Business Rule Best Practices

Here are my top things I would do when creating a business rule:
  1. Set the Scope to a specific form (unless there is a really good reason to make it Entity scope)
  2. Make it affect only one field on a specific form
  3. Give it a name using the one form / field it is tied to
  4. Give it a description that explains WHY it is necessary, not a description of what it does
  5. Include the inverse action (ex: lock AND unlock) after the condition
I recommend using a scope that is specific to a form, because it is common to have multiple forms, and if you set the scope to All Forms or Entity, then you run the risk of having the logic have unexpected results. 

The reason to make it affect only one field is that it is very common for someone to remove a field from a form without realizing that the field is part of a business rule, and it causes the entire business rule to stop running. If you have a complex business rule that updates multiple fields, then you run the risk that future form maintenance will break the rule. By making a separate business rule for each field is that you can very clearly "declare" that one fields behavior separate from every other field on the form, thus removing a field from a form will not break all the other logic on the form. This is the same way that Canvas apps operate, using declarative approach, instead of procedural. 

Naming the business rule something like "Main Form Account Sync locked" is more meaningful than trying to invent a name that describes the logic that affects 10 fields. 

A description should inform the person that is maintaining the code "why" you have the business rule, for example "After the account is synchronized, this field is read-only so that users don't try to change it back to No". Imagine trying to write a useful description for multiple fields. 

The reason to have both branches of the condition covered is that (in most cases) a user is likely to start to enter a value in a field which triggers the business rule, then they change their mind and want to choose a different option, but if there is no alternate action to 'undo' what was set in the Yes condition, then the user is stuck. 

Wednesday, August 21, 2024

PowerAutomate Flows Trigger Multiple Times - a proposed workaround



Revised 2/7/26

Every now and then, I get a Power Automate Flow that seems to trigger more than once, and most of the time it is not a problem, but sometimes they are running in parallel and end up creating duplicate records which is undesirable. It happens most often when a Dataverse trigger Change Type is "Added or Modified". I suspect that something in the API is updating the record right after creating it. I have tried using Delay actions, however, when I have a large number of flows running, there is a possibility that the delay will not be long enough, or it just lets the duplicate flows run together at a later time. 

There is a "Concurrency Control" feature under the trigger action settings. If you turn this on, you cannot turn it off (not even by deleting the trigger action), so make a backup first and consider your options. What this feature can do is make your flows run one after the other, rather than at the same time (in parallel). By turning setting the trigger's concurrency control to 1, and revise the flow to run a query first (LIST action) to see if any other flow finished the work already.

While it can solve this problem with flows running in parallel, I believe it will be a performance bottleneck; I would appreciate if someone could confirm this. Further, I don't want to do this to very many flows, and I cannot always tell when it will be a problem. Should I just settle for possible bottlenecks and make all of them single threading? Nah, I like getting the max performance possible.

A better solution:
I created an "Event Control" table with an alternate key on the Name field. If more than one flow tries to add more than one row to that table with the same alternate key value, then it will fail.

The new strategy is this: Using the ID of the triggering record and a the flow name as a 'key value', insert it into the Event Control table. If it succeeds, continue, else terminate. At the end of the flow, delete the record that was created. You could also potentially use the Event Control table to surface an admin dashboard to monitor the health of your system and present error messages, or highlight long running processes. Here is a simple example:

If the "Add event control" action fails, then another flow is already running and the Scope will exit to the Terminate action below. If it succeeds, then it will "perform some work" and delete the event control. Note that we want the delete action to run if something fails while performing the work. 

In some cases, I may have several different flows that need to run from the same triggering event. In this case, I will augment the 'Name' with both the record GUID and the name of the flow so that Flow "A" and "B" can both run when started from the same triggering event. 

I have noticed that the OOTB Power Automation / Automation Center will show errors for actions that fail, even if the action is followed by "Run After" to catch the failed action, so for that reason, using this strategy could look like more failures are happening, but if you create your own dashboard, you can get better information. I use Run After all over my flows (because I am used to using Try/Catch in other programming languages) and the Automation Center is not helpful for me. I would like to hear your thoughts on the subject.