Build Efficient Flows Using Filter Expression in Microsoft Dataverse Trigger
One of the key best practices for creating an efficient automated flow in Power Automate
(Dynamics 365 or Common Data Service) is to properly
set the options on the trigger of the flow to minimise unnecessary triggering and reduce execution time. As specified in
a guide to building enterprise-ready flows, you should always set attribute filters unless you are interested in any
change to the record. Aside from the attribute filtering, there is also a
Filter Expression parameter option in the Common Data Service (current environment) connector that you can use to filter records to process.
In this blog post, you will learn how to use the following functionalities in Power Automate.
- Filter Expression to filter further based on the value of the record and run the flow only for certain criteria
- Trigger Conditions to filter as an alternative option, what are the differences and when to use it
- Peek code option to get the expression of the Dynamic content
- Guard conditions to increase the readability of the flow
Let’s identify a few scenarios where we can apply our filtering and how it affects the efficiency of the process.
Scenario 1: The Case entity has multiple statuses (In
Progress, On Hold, Waiting for Details, Researching, Problem Solved,
Cancelled, etc.) and an automated flow needs to trigger when the Case is cancelled.
Scenario 2: There are multiple types of Contacts based on the custom OptionSet values (Client, Family, Professional, Employee) and an automated flow needs to trigger when the Employee Contact record is deleted.
Scenario 3: An automated flow needs to trigger when the Account record is created and there is no value in Parent Account lookup.
Scenario 2: There are multiple types of Contacts based on the custom OptionSet values (Client, Family, Professional, Employee) and an automated flow needs to trigger when the Employee Contact record is deleted.
Scenario 3: An automated flow needs to trigger when the Account record is created and there is no value in Parent Account lookup.
If no filter expression is used in the scenarios above, the automated flow
will trigger regardless of the value of the field and the unnecessary flow
runs need to be cancelled with a condition check. Let's see how we can optimise our flow and use Filter Expression or Trigger Conditions so that the flow will only run based on the criteria we want.
Sample Scenario: An automated flow needs to trigger when the Account record is deactivated.
This is how it will look like in a workflow designer if the Sample Scenario is
solved using the classic workflow process.
As a Dynamics 365 consultant who started working with Microsoft Dynamics CRM
workflow designer, the flows that I created initially were based on how I would design it using the classic workflow designer.
Over time, as I learn more and more, I managed to improve the design of my flow gradually. One of the things I learnt was how to
avoid nested conditionals by using Guard Conditions after coming across the blog post by
Jonas Rapp. So instead
of the flow above, the flow can be re-created in the following manner to increase
its readability.
However, the flow still runs whenever the status of the
Account record is changed to either Active or Inactive and
unnecessary Canceled flow runs are showing up in the run history. In Common Data Service (current environment) connector trigger, there is a parameter called Filter Expression which accepts an OData
style filter expression. By using that parameter, we can control the trigger to only fire if the filter expression evaluates to true.
By adding the filter expression in the above sample, the condition check in the flow is no longer necessary and the flow run history only shows the valid triggers according to the requirement.
If you are not familiar with OData style filter expression, you can also use the FetchXML Builder tool as mentioned in the other blog post. You can also learn more details about the Filter Expression in this video by Matt Collins-Jones.
Using the Filter Expression to filter the CDS trigger is recommended because most of those who use the Filter Query in the List records CDS action and those who query data using the Web API with filter property are already familiar with it.
Alternative Solution: Trigger Conditions
Even though Common Data Service (current environment) connector is the recommended one, there might be some use cases where the older "Common Data Service" connector has to be used instead of the (current environment) one.
There is no Filter Expression parameter in the old CDS connector trigger and in those cases, the Trigger Conditions can be used to filter the CDS trigger. Trigger Conditions is not specific to the CDS connector and it can be used in the trigger step of any other connectors too.
To add the Trigger Conditions, click on the ellipsis of the trigger and select Setting.
Then, fill up the Trigger Conditions using the flow expression.
It needs to start with @ symbol followed by the flow expression using triggerOutputs.
Multiple trigger conditions can be added if the flow needs to trigger only when ALL of the conditions are met.
If the flow can trigger as long as one of the conditions are met, or expression can be used.
@or(equals(triggerOutputs()?['body/statecode'], 1), equals(triggerOutputs()?['body/name'], 'Test Account'))
To learn more about the Trigger Conditions, check out this vlog by Reza Dorrani.
This approach might be easier for those who are proficient in flow expression but not familiar with OData style filter expression.
The flow checker can also validate if the syntax of the flow expression is incorrect (e.g. missing @, wrong spelling of the expression or missing parentheses, etc.). But it will not validate if the variable in the expression is not valid. There is no way to select from the list of Dynamic content and the whole flow expression needs to be typed in manually.
🛈 Bonus Tip
If you do not know what the expression for the Dynamic content is, add a Compose step and populate with the required Dynamic content. Then, use the peek code option to get the expression of the Dynamic content.
If you do not know what the expression for the Dynamic content is, add a Compose step and populate with the required Dynamic content. Then, use the peek code option to get the expression of the Dynamic content.
Since the Trigger Conditions can be used to filter based on the outputs of the trigger step, one of the advantages over Filter Expression is that a flow trigger can be filtered based on
- the label of the Optionset field. This is not recommended but technically possible (e.g. trigger the flow only if the record status label is "Inactive")
@equals(triggerOutputs()?['body/_statecode_label'], 'Inactive')
- the name value of the lookup field (e.g. trigger the flow only when the record is assigned to the team)
@equals(triggerOutputs()?['body/_ownerid_type'], 'teams')
For the same reason, Trigger Conditions cannot be used to filter for the record Delete event because the outputs of the Delete trigger step do not contain any useful information. (If you'd like to see more information in the outputs of the Delete trigger, go and vote this Power Automate Idea)
Summary
It is important not only to set attribute filters on the trigger to minimise unnecessary triggering but also to set the filter conditions of the trigger using Filter Expression with OData style filter expression (recommended way) or Trigger Conditions with flow expression (alternative way). By filtering the condition of the flow triggers, we can
- prevent the flow from triggering unnecessarily
- save a lot of API calls
- have a cleaner flow run history
Hi Linn,
ReplyDeleteTruly appreciate the content that you are sharing.
I do have a query regarding filtering attributes.
My scenario is that I have a flow which gets triggered for multiple filtering attributes. Is there a way identity which filtering attribute triggered the flow?
Please let me know your views on this.
Hi Sagar,
DeleteGlad to hear that my content is helpful.
I am afraid there is no easy way to identify the filtering attribute which triggers the flow. The workaround solution would be creating multiple flows with different filtering attributes which call the common child flow for the rest of the steps.