This guide covers all configuration options for CascadeFields, including best practices, UI behaviors, and advanced patterns. It reflects the latest features:
Related documentation:
-
XrmToolBox admin guide: QUICKSTART.md
-
Build/manual registration/contributing: PRODEV.md
-
Three left-pane tabs:
- Configuration: Select solution and parent entity
- Log: See real-time status and troubleshooting info
- JSON Preview: Live JSON for your current configuration
-
Checkboxes:
- Is Active: Toggle to enable/disable cascading for this configuration
- Cascade on Parent changes (Update): Control cascades triggered by parent updates
- Cascade on new Child records (Create): Control cascades triggered when child records are created
- Cascade when Child association changes (Relink): Control cascades triggered when a child is relinked
- Auto-delete Successful System Jobs: When enabled, successful async System Jobs created by the parent step are automatically deleted to prevent clutter
- Enable Detailed Tracing: Controls how much the plug-in writes to Dataverse tracing (
ITracingService). Enable for development, disable for production
-
Retrieve Configured Entity:
- If only one parent entity is configured, it loads automatically
- If multiple, a selector dialog appears (selecting a child row highlights the parent)
-
Changing Parent Entity:
- Loads that parent's configuration and children
- If no children are configured, prompts to add a child relationship immediately
Always use explicit lookup field names for reliability:
{
"parentEntity": "account",
"isActive": true,
"relatedEntities": [
{
"entityName": "contact",
"useRelationship": false,
"lookupFieldName": "parentcustomerid",
"fieldMappings": [...]
}
]
}Benefits:
- ✅ Works with parent updates, child creates, and child relinks
- ✅ No metadata lookups needed
- ✅ Better performance
- ✅ More reliable
- ✅ Easier to troubleshoot
| Parent Entity | Child Entity | Lookup Field |
|---|---|---|
| account | contact | parentcustomerid |
| account | opportunity | accountid or parentaccountid |
| contact | contact | parentcontactid |
| incident | incidentresolution | incidentid |
| opportunity | opportunityproduct | opportunityid |
Tip: To find lookup field names:
- Open Advanced Find
- Select child entity
- Add condition for parent entity relationship
- Field name shown in editor
field|operator|value;field2|operator2|value2
| Operator | Aliases | Description |
|---|---|---|
eq |
equal, = |
Equal to |
ne |
notequal, != |
Not equal to |
gt |
greaterthan, > |
Greater than |
lt |
lessthan, < |
Less than |
in |
- | Value is in a comma-separated list |
notin |
- | Value is not in a comma-separated list |
null |
- | Is null |
notnull |
- | Is not null |
like |
- | Pattern match |
Active records only:
"filterCriteria": "statecode|eq|0"Multiple conditions:
"filterCriteria": "statecode|eq|0;revenue|gt|50000"Check for null:
"filterCriteria": "primarycontactid|notnull|null"Match multiple values (IN):
"filterCriteria": "address1_stateorprovince|in|CA,NY,TX"Exclude specific values (NOT IN):
"filterCriteria": "industrycode|notin|6,9"{
"sourceField": "address1_city",
"targetField": "address1_city",
"isTriggerField": true
}- Set
isTriggerField: truefor fields that should trigger the cascade - At least one trigger field recommended
- If no trigger fields specified, ALL changes trigger cascade
The plugin automatically handles type conversions:
Lookup/OptionSet → Text:
- Lookup: Uses display name (or formatted value, or ID as fallback)
- OptionSet: Uses label text (or numeric value as fallback)
- Automatically truncates if target field is too short
Companion "Name" Fields:
You can map the display-name companion of a lookup or choice field directly to a
text field. These virtual fields end with name and are resolved at runtime:
| Source field | Base field | Resolves to |
|---|---|---|
parentcustomeridname |
parentcustomerid |
Lookup display name |
statuscodename |
statuscode |
Status reason label |
industrycodename |
industrycode |
Choice option label |
These companion fields do not appear in entity metadata. The plugin detects them
automatically by stripping the name suffix and reading the base attribute's
formatted value.
Same-Type Mappings:
- Text → Text
- Number → Number
- DateTime → DateTime
- Lookup → Lookup
- OptionSet → OptionSet
- Money → Money
Cascade the same fields to different child entity types:
{
"parentEntity": "account",
"relatedEntities": [
{
"entityName": "contact",
"lookupFieldName": "parentcustomerid",
"useRelationship": false,
"fieldMappings": [
{
"sourceField": "address1_city",
"targetField": "address1_city",
"isTriggerField": true
}
]
},
{
"entityName": "opportunity",
"lookupFieldName": "accountid",
"useRelationship": false,
"fieldMappings": [
{
"sourceField": "address1_city",
"targetField": "address1_city",
"isTriggerField": true
}
]
}
]
}Each child entity gets different fields:
{
"parentEntity": "account",
"relatedEntities": [
{
"entityName": "contact",
"lookupFieldName": "parentcustomerid",
"useRelationship": false,
"fieldMappings": [
{
"sourceField": "address1_city",
"targetField": "address1_city",
"isTriggerField": true
},
{
"sourceField": "telephone1",
"targetField": "telephone1",
"isTriggerField": false
}
]
},
{
"entityName": "opportunity",
"lookupFieldName": "accountid",
"useRelationship": false,
"fieldMappings": [
{
"sourceField": "creditlimit",
"targetField": "budgetamount",
"isTriggerField": true
}
]
}
]
}Only update specific child records:
{
"entityName": "opportunity",
"lookupFieldName": "accountid",
"useRelationship": false,
"filterCriteria": "statecode|eq|0;salesstage|lt|4",
"fieldMappings": [...]
}This only updates active opportunities in early sales stages.
Only cascade when specific fields change, but copy all mapped fields:
{
"fieldMappings": [
{
"sourceField": "creditonhold",
"targetField": "creditonhold",
"isTriggerField": true
},
{
"sourceField": "creditlimit",
"targetField": "creditlimit",
"isTriggerField": false
},
{
"sourceField": "paymenttermscode",
"targetField": "paymenttermscode",
"isTriggerField": false
}
]
}Cascade only triggers when creditonhold changes, but all three fields are copied.
For production environments, disable verbose tracing:
{
"parentEntity": "account",
"isActive": true,
"enableTracing": false,
"relatedEntities": [...]
}Note: Error logging is always enabled regardless of this setting.
The bypassCustomPluginExecution flag is an opt-in setting that tells
Dataverse to skip all downstream custom plugins and workflows when updating
child records during a cascade.
{
"parentEntity": "account",
"isActive": true,
"bypassCustomPluginExecution": true,
"relatedEntities": [...]
}Default: false — child updates run through the normal Dataverse plugin
pipeline.
⚠️ Requirements and warnings:
- The executing user must hold the
prvBypassCustomPluginExecutionprivilege (typically System Administrator only). If the user lacks this privilege, every child update will fail.- When enabled, all custom plugins, workflows, and business rules on the child entity are skipped for cascade-triggered updates.
- Use this only when you have a specific reason (e.g., avoiding recursive plugin execution or import-pipeline conflicts).
UI Guidance:
- Use the left pane checkboxes to control tracing and activation
- Use the right pane to add relationships, field mappings, and filters
- When prompted to add a relationship (after selecting a parent with no children), follow the dialog to select a child entity
Before deploying:
- ✅ Validate JSON syntax (use jsonlint.com)
- ✅ Verify parent entity name is correct
- ✅ Verify all child entity names are correct
- ✅ Verify all lookup field names are correct
- ✅ Verify source/target field names match exactly (case-sensitive)
- ✅ Test filter criteria in Advanced Find first
- ✅ At least one field mapping per related entity
- ✅ Test in non-production environment
- Use filtering attributes - Register plugin steps with only trigger fields
- Apply filter criteria - Target specific child records
- Limit child record count - Plugin has 5000 record safety limit per query.
If a query returns exactly 5000 records, a warning is logged:
Query returned 5000 records (the safety limit). Some child records may not have been updated.Use filter criteria to narrow the scope if you hit this limit. - Use batch updates - Automatically enabled (50 records/batch)
Check execution time in trace logs:
Note (Org Setting Required): To capture and view Dataverse plug-in traces, your environment must have Plug-in trace log enabled. If set to Off, nothing is stored. If set to Exception, logs are stored only when the plug-in throws. Use All while troubleshooting. You can configure this in the Power Platform admin center: Environments → (your environment) → Settings → Plug-in trace log.
[INFO] Update complete: 150 successful, 0 failed
[INFO] === Plugin Execution Completed Successfully === [+1250ms]
For large record sets (>1000 children):
- Execution time typically 2-5 seconds
- 98% reduction in API calls vs. individual updates
- Asynchronous processing prevents UI blocking
- Use the toolbar button to load existing configurations
- If only one parent is configured, it loads automatically
- If multiple, a dialog appears—select a parent row (child row selection highlights parent)
- Loads all configured children for that parent
- If none, prompts to add a child relationship
Parent Update:
- Check filtering attributes include trigger fields
- Verify
statecodefilter matches child records - Ensure parent record has related children
Child Create/Update:
- Verify
lookupFieldNameis specified and correct - Check if child step is registered (look for "Child Create" and "Child Relink" steps)
- Ensure lookup field is populated on create or changed on update
- Verify field names are exact (case-sensitive)
- Check source field exists on parent
- Check target field exists on child
- Verify field types are compatible
- Review trace logs for errors
- Ensure executing user has Read on parent entity
- Ensure executing user has Update on child entities
- Check field-level security settings
- Test filter in Advanced Find first
- Check field names are correct (no typos)
- Verify operator syntax (use
eq, notequals) - Review trace logs for filter parsing errors
{
"id": "account-to-contact-full",
"name": "Account to Contact - Production",
"parentEntity": "account",
"isActive": true,
"enableTracing": false,
"cascadeOnParentUpdate": true,
"cascadeOnChildCreate": true,
"cascadeOnChildRelink": true,
"deleteAsyncOperationIfSuccessful": true,
"bypassCustomPluginExecution": false,
"relatedEntities": [
{
"entityName": "contact",
"useRelationship": false,
"lookupFieldName": "parentcustomerid",
"filterCriteria": "statecode|eq|0",
"fieldMappings": [
{
"sourceField": "address1_city",
"targetField": "address1_city",
"isTriggerField": true
},
{
"sourceField": "address1_stateorprovince",
"targetField": "address1_stateorprovince",
"isTriggerField": true
},
{
"sourceField": "address1_postalcode",
"targetField": "address1_postalcode",
"isTriggerField": false
},
{
"sourceField": "telephone1",
"targetField": "telephone1",
"isTriggerField": false
}
]
}
]
}This configuration:
- Cascades when city or state changes
- Copies all four fields when triggered
- Only updates active contacts
- Disabled verbose tracing for production
- Supports parent updates, child creates, and child relinks
- Auto-deletes successful async system jobs
- Child updates run through normal plugin pipeline (bypass disabled)