Deleted Event

Summary

The Deleted event fires after an entity has been removed from the data context. Use it for post-deletion cleanup, transferring responsibilities to other entities, or logging. The deletion cannot be cancelled at this point — use the Deleting event if you need to prevent deletion.

When does it fire?

The Deleted event fires after the entity and any AlsoDelete entities have been successfully removed from the context. The entity data is still accessible in the event handler, but the entity is no longer tracked by the context.

Delete request → Deleting (can abort) → Entity removed → Deleted (post-cleanup)

Syntax

productUom.OnDeleted().Do(deletedArgs => { /* logic */ });

Do overloads

Signature Description
.Do(args => { }) React to the deletion via args.Entity to access the deleted entity

The args parameter provides:

  • args.Entity — the entity that was just deleted (still readable but no longer tracked)

Scenarios

1. Transferring the default selling unit designation

When a UOM that was the default selling unit is deleted, transfer that designation to the product's main UOM so there is always a default.

[Logic]
public class ProductUomBL(ProductUom.Logic productUom)
{
    [RegisterLogic]
    public void TransferDefaultSellingUnit()
    {
        productUom.OnDeleted().Do(c =>
        {
            if (c.Entity.SellableOption == SellableOption.DefaultSellingUnit)
            {
                var mainUom = c.Entity.Product?.Uoms
                    .FirstOrDefault(u => u.Operation == UomOperation.Main);
                if (mainUom != null)
                    mainUom.SellableOption = SellableOption.DefaultSellingUnit;
            }
        });
    }
}

2. Logging the deletion of a customer

Record when a customer entity is removed for audit purposes.

[Logic]
public class CustomerBL(Customer.Logic customer)
{
    [RegisterLogic]
    public void LogDeletion()
    {
        customer.OnDeleted().Do(c =>
        {
            var logger = c.GetService<ILogger<CustomerBL>>();
            logger.LogInformation("Customer {Id} ({Name}) was deleted",
                c.Entity.Id, c.Entity.FullName);
        });
    }
}

3. Cleaning up orphaned contacts

When a customer is deleted, check whether the primary contact is used by any other entity. If not, mark it for review.

[Logic]
public class CustomerBL(Customer.Logic customer)
{
    [RegisterLogic]
    public void FlagOrphanedContacts()
    {
        customer.OnDeleted().Do(c =>
        {
            var contact = c.Entity.PrimaryContact;
            if (contact == null) return;

            var stillReferenced = c.GetEntities<Customer>()
                .Any(cust => cust.PrimaryContact == contact);
            if (!stillReferenced)
                contact.Status = ContactStatus.NeedsReview;
        });
    }
}

Deleted vs. Deleting

Deleting Deleted
When Before removal After removal
Can cancel? Yes (AbortIf) No
Can delete others? Yes (AlsoDelete) No — use Deleting for that
Use for Prevention, cascading deletes Cleanup, logging, state transfer

Notes

  • Do not use OnDeleted to delete related entities. Use OnDeleting().AlsoDelete() instead.
  • Do not use OnDeleted to update aggregates. Use Compute with .DirtyWithRelation(o => o.Details).DirtyBy(...) to update aggregates when a child entity is deleted.
  • The deleted entity's properties are still readable in the handler, but the entity is no longer part of the data context.
  • OnDeleted runs after all AlsoDelete entities have been removed.