Cascade rules determine what happens to child records when you assign, share, delete, or reparent records in Dataverse — and misconfiguring them is the source of some of the most baffling data quality and security issues in production model-driven apps. This lesson teaches you how each of the five cascade actions works, when to use Parental vs. Referential vs. Custom behavior, and how to configure and troubleshoot cascade rules across multi-level table hierarchies.

Here's a scenario that plays out in production Dataverse environments more often than anyone likes to admit: a salesperson leaves the company, so an admin reassigns their Account records to the new rep. Three weeks later, the support team notices that hundreds of open Cases still show the departed salesperson as owner. The data is inconsistent, the team is frustrated, and nobody can explain why the fix "only half worked." The culprit, almost always, is relationship behavior configuration — specifically, cascade rules that weren't set intentionally.
Cascade rules are the mechanism Dataverse uses to decide what happens to child records when something changes on the parent. When you delete a Project, do its Tasks disappear too? When you assign an Account to a new owner, do its Contacts come along? When you share an Opportunity with a team, does that access extend to the attached Quote records? These aren't abstract data-modeling questions — they have direct consequences for data integrity, security, and user experience inside your model-driven apps.
By the end of this lesson, you'll understand how each cascade behavior type works at the platform level, how to configure them intentionally for real-world parent-child relationships, and how to debug the downstream effects when behavior doesn't match expectations. We'll use a realistic project management data model throughout — Projects, Milestones, Tasks, and Attachments — to illustrate each scenario concretely.
What you'll learn:
You should be comfortable with Dataverse table relationships before diving in here. If you need a refresher on how one-to-many and many-to-many relationships work at a structural level, start with Designing a Dataverse Data Model: Relationships, Lookups, and Choice Columns and the deeper treatment in Configuring Dataverse Table Relationships in Model-Driven Apps: One-to-Many, Many-to-Many, and Cascade Behaviors Explained. You should also have a working understanding of Dataverse Security: Business Units, Security Roles, and Teams, since ownership and sharing concepts are central to how cascade rules function.
Before you touch any configuration, it's worth building an accurate mental model of what cascade rules do.
Cascade rules are platform-level behaviors attached to a one-to-many relationship that execute automatically when certain operations are performed on the parent record. They are not Power Automate flows, not plug-ins, not business rules — they run synchronously at the database tier the moment the triggering operation commits. That distinction matters enormously for performance and predictability.
The five operations that can be cascaded are:
| Operation | Triggered When... |
|---|---|
| Assign | A parent record's owner is changed |
| Share | A parent record is shared with a user or team |
| Unshare | A parent record's sharing is revoked |
| Reparent | A child record's lookup value is changed to point to a different parent |
| Delete | A parent record is deleted |
Each operation has its own behavior setting, and they're independent of each other. You can configure Delete to cascade while Share does nothing, or vice versa. Understanding each one separately is the key to intentional configuration.
Note
Cascade rules only apply to one-to-many (1:N) relationships. Many-to-many relationships use intersect tables and have a different deletion model — the intersect record is removed when either participating record is deleted, but neither participant cascades to the other. For advanced M:N configuration, see Configuring Dataverse Many-to-Many Relationships with Intersect Tables in Model-Driven Apps.
For each cascade action, Dataverse offers a set of behavior options. Not all options are available for every action, but understanding the full vocabulary first will make the configuration choices clearer.
Every child record that belongs to the parent gets the same operation applied. If you assign a Project to a new owner, all Milestones and Tasks owned by the old Project owner get reassigned to the new one.
Only child records in an active state get the cascade. Completed, cancelled, or inactive child records are left as-is. This is extremely useful when you want historical records to preserve their original ownership for audit purposes.
The cascade applies only to child records that are owned by the same user as the parent. Child records that were individually assigned to other users are left alone. This is the most nuanced and often the most correct setting for real-world scenarios where team members have been assigned individual tasks from a shared project.
Nothing happens to child records when the parent changes. For Delete specifically, this option comes in two flavors:
Warning
Remove Link for Delete is often the most dangerous choice. You end up with orphaned child records that have no parent reference, which can cause reporting gaps, broken rollup columns, and confusing user experience. Use it only when orphaned children are genuinely a valid business state.
No propagation occurs. Each child record's sharing, linking, or ownership is managed independently.
When you create a 1:N relationship, Dataverse offers three named behavior presets before letting you go fully custom. Understanding what these actually configure under the hood prevents a lot of confusion.
This is the "tight coupling" preset. Every cascade action is set to Cascade All. The child is fully dependent on the parent — whatever happens to the parent happens to all children, without exception.
| Action | Setting |
|---|---|
| Assign | Cascade All |
| Share | Cascade All |
| Unshare | Cascade All |
| Reparent | Cascade All |
| Delete | Cascade All |
Use Parental when child records genuinely have no independent existence. The relationship between Invoice and Invoice Line Item is the canonical example — a line item is meaningless without its invoice. In our project management model, Project → Milestone is a good candidate: a milestone that isn't associated with any project is useless data.
This is the "loose coupling" preset. The relationship enforces that the parent exists (referential integrity), but no cascade propagation occurs.
| Action | Setting |
|---|---|
| Assign | No Cascade |
| Share | No Cascade |
| Unshare | No Cascade |
| Reparent | No Cascade |
| Delete | Restrict |
Use Referential when the child can exist somewhat independently and you want to prevent orphaning by blocking deletion of a parent that still has children. A Contact linked to an Account is the standard example — the Contact can be managed separately, and you shouldn't be able to delete an Account with active Contacts without explicitly handling them first.
This is a variant where Delete is set to Restrict but the other four actions also propagate. It's less commonly used as a preset because most scenarios that want restrict-on-delete usually don't want full cascades elsewhere. You'll typically find yourself in Custom territory here.
Custom lets you set each of the five actions independently. This is where most mature data models end up, because real-world business rules rarely fit neatly into "everything cascades" or "nothing cascades."
Key insight
The preset names (Parental, Referential) are convenience labels — they just pre-fill the five action dropdowns. After selecting a preset, you can immediately change individual actions. Don't let the label constrain your thinking. A relationship that starts as Parental can have its Share behavior changed to Cascade User-Owned without becoming a different "type" in any meaningful sense.
Let's walk through the actual configuration steps using our project management model.
Scenario: You have a crm_project table and a crm_task table connected by a 1:N relationship via a crm_projectid lookup on the Task table. You want Tasks to:
To configure this:
crm_project table (the "one" side of the relationship).crm_task and click the three-dot menu, then Edit.Tip
Changes to relationship behaviors don't require republishing the app — they take effect at the platform level immediately after saving the table. However, you should always test in a non-production environment first, because behavior changes on relationships with existing data can have immediate consequences on record access.
The Assign cascade is about record ownership, which in Dataverse is the ownerid column present on all user-owned tables. When a parent record is reassigned, the cascade determines whether child records follow.
In our project management model, consider: a Project Manager is reassigned when they move to a different department. The admin reassigns their Projects to the new PM. With Cascade All, every Task — including ones the old PM had already individually delegated to junior team members — gets yanked back and reassigned to the new PM. That's almost certainly wrong.
Cascade User-Owned is the right call here. It says: "Only reassign Tasks that are still owned by the same person who owned the Project." Tasks that a team member was specifically assigned to stay with that person. This respects intentional individual assignments while still catching the "forgotten" tasks that were never explicitly delegated.
The tricky part: Cascade User-Owned compares the child's current owner to the parent's pre-change owner, not the post-change owner. So if Project is owned by Alice and you reassign it to Bob, Cascade User-Owned will find all Tasks owned by Alice and reassign them to Bob. Tasks owned by Carol or David stay put.
Sharing is Dataverse's mechanism for granting access to records without transferring ownership. As covered in detail in Column-Level Security and Record Sharing in Dataverse, you can share a specific record with a user or team at a given access level.
When you share a Project record with the leadership team so they can view progress, do you also want them to see every Task? That depends entirely on your security model.
If your security roles already grant leadership read access to all Task records within their business unit, sharing the Project with them is redundant for Tasks — no cascade needed (No Cascade). But if Task records are scoped very tightly and the only way for leadership to see them is through explicit sharing of the parent Project, then Cascade All on Share is the right choice.
The symmetric rule applies to Unshare. If you cascade Share, you almost always want to cascade Unshare with the same setting. Sharing a Project with someone and then revoking that share while they retain access to all the Tasks underneath is a security hole.
Reparent fires when a user changes the value of the lookup field on the child record — essentially moving a Task from one Project to another. The cascade behavior here controls what happens to ownership and sharing when that move occurs.
With Cascade All on Reparent: when a Task is moved to a new Project, the Task's owner is updated to match the new Project's owner, and all sharing on the Task is replaced with the new Project's sharing.
With No Cascade on Reparent: moving a Task to a different Project changes only the lookup value. The Task keeps its current owner and its existing sharing configuration, regardless of what the new Project looks like.
For most scenarios involving task reassignment in project management, No Cascade on Reparent is correct. A task being moved to a different project is an administrative action, not an ownership transfer event. The person who was doing the task is still doing it.
Warning
If you have Cascade All on both Assign and Reparent, you can create a compounding effect. Moving a child to a new parent (Reparent cascade fires, adjusting ownership to the new parent's owner) can then trigger an effective Assign operation if the new owner differs from the old one. Test this combination carefully in environments with complex security configurations.
Delete cascade is where data loss lives. Get this wrong and you can either:
For our project management model:
This layered approach — cascading through the operational hierarchy but restricting at the financial/audit boundary — is a pattern you'll see in well-designed Dataverse solutions.
Key insight
When Delete is set to Restrict, the error message that surfaces in the model-driven app is generic: "Cannot delete [Record Name] because it is associated with other records." This is confusing to end users. Consider adding a business rule or a model-driven app notification that proactively tells users what they need to resolve before deletion is possible. Even better, surface child record counts on a dashboard so users can see the dependency before attempting to delete.
When you're migrating solutions between environments or configuring relationships at scale, knowing the underlying XML is valuable. Relationship behaviors are stored in the solution's customizations.xml inside the relationship definition.
Here's what a custom behavior configuration looks like for the Project-to-Task relationship:
<EntityRelationship Name="crm_project_crm_task">
<EntityRelationshipType>OneToMany</EntityRelationshipType>
<IsHierarchical>0</IsHierarchical>
<ReferencingEntityName>crm_task</ReferencingEntityName>
<ReferencedEntityName>crm_project</ReferencedEntityName>
<CascadeAssign>UserOwned</CascadeAssign>
<CascadeDelete>All</CascadeDelete>
<CascadeReparent>NoCascade</CascadeReparent>
<CascadeShare>NoCascade</CascadeShare>
<CascadeUnshare>NoCascade</CascadeUnshare>
<IsValidForAdvancedFind>1</IsValidForAdvancedFind>
<ReferencingAttributeName>crm_projectid</ReferencingAttributeName>
<RelationshipDescription>
<Descriptions />
</RelationshipDescription>
<CascadeRollupView>NoCascade</CascadeRollupView>
<EntityRelationshipRoles>
<EntityRelationshipRole>
<NavPropName>crm_project_crm_task</NavPropName>
<RelationshipRoleType>0</RelationshipRoleType>
</EntityRelationshipRole>
<EntityRelationshipRole>
<NavPropName>crm_projectid_crm_project</NavPropName>
<RelationshipRoleType>1</RelationshipRoleType>
</EntityRelationshipRole>
</EntityRelationshipRoles>
</EntityRelationship>
The cascade values in XML use these string mappings:
| UI Label | XML Value |
|---|---|
| Cascade All | All |
| Cascade Active | Active |
| Cascade User-Owned | UserOwned |
| No Cascade | NoCascade |
| Restrict | Restrict |
| Remove Link | RemoveLink |
Tip
If you're deploying solutions via Azure DevOps pipelines or GitHub Actions, you can validate that relationship behaviors weren't accidentally changed during a merge by adding a step that parses the customizations.xml and asserts expected cascade values. This is especially valuable in team environments where multiple developers might be working on the same data model.
This is the dimension that catches practitioners off guard. Cascade rules and security roles interact in ways that can produce unexpected results — records that should be reassigned aren't, or records get shared more broadly than intended.
When Cascade All fires for an Assign operation, Dataverse reassigns all child records regardless of whether the new owner has a security role that grants access to those child table records. The records get a new ownerid — access is determined at runtime when the new owner tries to open them.
This means you can assign a Project to someone who has no security role for the Task table, and all the Tasks will now show that person as owner. When they try to view those tasks, they'll get an access error. The cascade executed successfully — the security enforcement happens separately.
When Cascade All fires for Share, Dataverse creates principalobjectaccess records (the internal sharing table) for every child record. This can produce a very large number of sharing records if you have many child records. In large environments, aggressive use of Share cascading can cause the principalobjectaccess table to grow significantly, which has query performance implications.
For solutions where sharing is needed at scale, consider whether designing Dataverse hierarchical relationships and rollup security might be a better approach than sharing cascades. Hierarchical security lets managers automatically inherit access to records owned by subordinates, which can replace a large chunk of explicit sharing logic.
There's a sixth operation you won't find in the cascade configuration UI: Merge. When you merge two parent records (common with Accounts and Contacts in Sales scenarios), Dataverse uses a fixed behavior: child records are reparented to the surviving record, and the behavior for the Reparent cascade setting applies. If you have Reparent set to Cascade All, the merge will also reassign all children to the surviving record's owner. Be aware of this when merging in production.
Let's build and test a realistic cascade configuration for a three-level hierarchy.
Data model:
crm_project (parent) → crm_milestone (child) → crm_task (grandchild)Business requirements:
Navigate to the crm_project table, open Relationships, edit the relationship to crm_milestone, and set:
Save the table.
Navigate to the crm_milestone table, open Relationships, edit the relationship to crm_task, and set the same values as Step 1.
Create a test Project with two Milestones. Under each Milestone, create two Tasks. You should have 1 Project, 2 Milestones, 4 Tasks.
Delete the Project. Navigate to the Task view — filter to show all records including inactive. All four Tasks should be gone. Navigate to Milestones — both should be gone.
Tip
Before testing Delete cascades on real data, always export the records to Excel first. Dataverse has a recycle bin feature in some environments, but cascade-deleted records may not appear there depending on your environment configuration. Don't rely on recovery — test in a sandbox.
Create a new Project owned by User A. Create two Milestones both owned by User A. Create four Tasks: two owned by User A, one owned by User B, one owned by User C.
Assign the Project to User D.
Expected result:
Verify by checking the Tasks view filtered by owner.
Share the Project with a test team at Read access level.
Navigate to Advanced Settings → Security → Access Teams (or use the sharing dialog in the record). After sharing the Project, check one of the Tasks. Open the task record and select Share from the command bar. The sharing dialog should show the test team already has Read access — this was inherited from the Project cascade.
Cascade operations are synchronous for small record sets, but when a parent has thousands of children, Dataverse processes the cascade asynchronously. The API call returns success, but child records are updated in batches over the next several minutes. Users who check immediately after a large assign operation may see mixed ownership states — some children updated, some not yet.
Diagnosis: Check the System Jobs view in the maker portal (Settings → System Jobs). Filter by status "In Progress" or "Waiting." You'll see the cascade operation listed.
Fix: Communicate to users that large cascade operations take time. Add a view or dashboard that makes ownership state visible so the team can monitor completion.
A team configures a Customer → Support Case relationship as Parental because it seems logical — Cases belong to Customers. But then they assign Cases to individual support agents and share Cases with specialist teams. Every time a Customer is reassigned to a new account owner, all their Cases silently change owner too.
Diagnosis: Check the relationship behavior. If it's Parental (all cascades set to Cascade All), the Assign and Share cascades are probably doing more than intended.
Fix: Switch Assign to Cascade User-Owned and Share to No Cascade. Cases that were individually assigned to agents will stay with those agents.
An admin configures a Project → Time Entry relationship with Delete set to Restrict. When a user tries to delete a completed Project, they get a generic error. They escalate, the helpdesk can't explain it, and trust in the system erodes.
Diagnosis: Look at the relationship behavior for the entity that's blocking deletion. A Restrict setting is correct behavior — the problem is the UX.
Fix: Add a subgrid on the Project form showing Time Entries, so users can see the dependency. Add a business rule or notification that proactively warns before deletion. Consider adding an "Archive" status so Projects can be deactivated (which doesn't trigger Delete cascade) rather than deleted.
You change a relationship from Referential (No Cascade on Delete) to Parental (Cascade All on Delete). Existing records are unaffected — the new behavior applies only to future operations. Existing child records still reference the parent as before. There's no retroactive enforcement.
Diagnosis: After changing behaviors, run a test by performing the operation on a test record. If children don't cascade, double-check that the change was saved (publish) and that you're testing the right relationship.
Fix: No fix needed — this is expected behavior. Behavior changes are not retroactively applied. If you need to "backfill" the effect of a behavior change (e.g., reassigning all children of a recently reassigned parent), you'll need a Power Automate flow or bulk update via dataflow. See Importing and Migrating Data into Dataverse: Excel Import, Dataflows, and Upserts for bulk update patterns.
In the solution XML, you may notice a <CascadeRollupView> element. This controls whether changes to child records trigger recalculation of rollup columns on the parent. If you have rollup columns that aggregate child data (like total hours across Tasks for a Project), this setting must be All for those rollups to stay current.
Diagnosis: Rollup column values on the parent look stale. Check the CascadeRollupView setting in the relationship XML.
Fix: Set CascadeRollupView to All in the relationship definition. Note that this is separate from the five user-visible cascade actions and is only accessible via solution XML or the SDK — the maker portal doesn't surface it directly. For more on rollup columns, see Formula Columns and Rollup Columns in Dataverse: Calculated Data Without Code.
One underappreciated complexity: if your solution is deployed as a managed solution on top of a base solution, relationship behaviors defined in the base layer can be overridden in a customization layer — but only if the base solution publisher has marked them as customizable.
When you import a managed solution that changes a relationship behavior, the behavior change applies immediately to all existing records and future operations. This is a breaking change if downstream solutions depend on the original behavior.
Warning
If you ship managed solutions to customers, lock down your relationship behaviors using managed properties. An ISV who ships a Project Management solution expecting Parental behavior on Project → Task, but a customer changes it to Referential, will end up with orphaned Task records when Projects are deleted — and the ISV's support team will spend hours diagnosing what looks like a platform bug. For details on controlling solution customizability, see Configuring Dataverse Managed Properties and Solution Component Locking.
Rather than configuring behaviors relationship by relationship without a plan, use this framework:
1. Classify each relationship by child independence.
2. Map your ownership model.
3. Define your deletion policy.
4. Define your sharing policy.
5. Document the decisions in your solution design. Add a relationship matrix to your data model documentation — table name, related table, and the five cascade settings. This becomes invaluable during maintenance when someone asks "why can't I delete this record?" six months after go-live.
Cascade rules are one of those configuration areas where small decisions have large consequences. The five actions — Assign, Share, Unshare, Reparent, and Delete — each control a distinct dimension of what it means for a child record to follow its parent through the lifecycle of the data. Configuring them thoughtlessly (or leaving them at defaults) is the source of a surprisingly large number of production data quality and security issues.
The key takeaways from this lesson:
From here, consider exploring how server-side logic can complement cascade rules. For complex scenarios where cascade rules alone aren't sufficient — for example, when you need to conditionally cascade based on a field value — Configuring Dataverse Table Event Plugins and Real-Time Workflows in Model-Driven Apps covers how to enforce that kind of logic at the data layer. Cascade rules and plug-ins work best as complements, not substitutes.