Elements: Delete relations when deleting an element folder (closes #23387) - #23394
Conversation
|
Claude finished @AndyButland's task in 2m 46s —— View job PR ReviewTarget: Adds a
No critical or important issues found. Suggestions
ApprovedClean, minimal, well-targeted fix. The integration test is thorough — it wires |
There was a problem hiding this comment.
Pull request overview
Fixes element folder deletion failing with FK violations when a previously-contained element was moved to the recycle bin, by ensuring relations referencing the container node are cleaned up as part of the standard container delete flow.
Changes:
- Add a
protected virtual OnDeletingContainer(EntityContainer)hook toEntityTypeContainerService.DeleteAsyncand invoke it inside the delete scope immediately before deleting the container node. - Override the hook in
ElementContainerServiceto reuse existing relation-cleanup logic (DeleteContainerRelations) for all element-folder deletions (not only recycle-bin deletions). - Add an integration test covering “trash element → delete original folder” and wire up trash-related notification handlers needed to reproduce the relation creation.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ElementContainerServiceTests.Delete.cs | Adds an integration test verifying element-folder deletion cleans up relations created when child elements are trashed. |
| tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ElementContainerServiceTests.cs | Registers RelateOnTrashNotificationHandler in the fixture and exposes IElementRecycleBinQueryService for assertions. |
| src/Umbraco.Core/Services/EntityTypeContainerService.cs | Introduces and invokes an overridable pre-delete hook (OnDeletingContainer) within the delete scope. |
| src/Umbraco.Core/Services/ElementContainerService.cs | Overrides the pre-delete hook to delete all relations referencing the container node before removal. |
Delete via the Management API bypassed the relation cleanup that DeleteFromRecycleBinAsync already had, causing an FK violation when deleting a folder whose only child had been trashed. Both entry points now go through a single DeleteLockedAsync, gated by a requireEmpty flag, so relation cleanup can't be missed by either.
lauraneto
left a comment
There was a problem hiding this comment.
This works well, but when looking further into it, it feels like an oversight that DeleteAsync is not using ElementContainerService.DeleteLockedAsync as well. This could cause more discrepancies between the different flows in the future.
I've created a branch on top of this one adjusting the code so that DeleteLockedAsync is used, instead of adding more logic to EntityTypeContainerService: v18/bugfix/23387-clear-relations-on-entity-container-delete...v18/bugfix/23387-clear-relations-on-entity-container-delete-v2
Let me know what you think!
|
Thanks @lauraneto - yes, on balance I like your approach better. I'll merge it in and see if there are any other improvements to make (the two primitive booleans of There is one downside which is similar to your concern about divergence, which is that we now won't be calling the base |
Use repository over service for empty check, so avoid additional scope.
|
I've applied your update and a couple of follow-up amends @lauraneto, and retested using the steps in the PR description. |
|



Description
Deleting an element folder through the Management API (
DELETE /umbraco/management/api/v1/element/folder/{id}) returned a 500 when the folder had previously contained an element that was moved to the recycle bin.Root cause: when an element is trashed,
RelateOnTrashNotificationHandlercreates arelateParentElementContainerOnElementDeleterelation whose parent is the folder node (so the element can be restored to its original location). The plain folder-delete endpoint resolves to the baseEntityTypeContainerService.DeleteAsync, which removed the folder node without cleaning upumbracoRelation, so the lingering relation violated theFK_umbracoRelation_umbracoNodeconstraint. The folder passes the existing "not empty" guard because the trashed element lives under the recycle bin, not under the folder. (The recycle-bin delete path already cleaned relations viaElementContainerService.DeleteContainerRelations— added in #22154 — but the direct-delete path never did.)Fix: a
protected virtual OnDeletingContainer(EntityContainer)hook is added toEntityTypeContainerService.DeleteAsync, invoked inside the delete scope immediately before the container node is removed.ElementContainerServiceoverrides it to reuse the existingDeleteContainerRelations, so all element-folder deletions clean up their relations consistently. The hook is a no-op for content-type/media-type/data-type folders, which never appear inumbracoRelation.After the fix the folder deletes successfully and the trashed element is left intact; having lost its original-parent relation it is restorable to the root or another selected location — mirroring existing document/media behaviour when an original parent no longer exists.
Fixes #23387
Testing
Automated
Added a new integration test
ElementContainerServiceTests.Can_Delete_Container_After_Child_Element_Trashed:umbracoRelationrows reference the folder, the element still exists in the recycle bin, and its original-parent lookup now reportsNoParentRecycleRelation(i.e. it will restore to root).Verified red before the fix (FK violation) and green after; the existing
ElementContainerServiceTestssuite continues to pass.Manual
DELETE /umbraco/management/api/v1/element/folder/{id}for the folder (or delete it from the backoffice).