Deleting Event
Summary
The Deleting event fires before an entity is removed from the data context. Use it to prevent deletion when business rules forbid it, or to specify additional entities that should be deleted along with the current entity.
When does it fire?
The Deleting event fires when context.Delete(entity) is called, before the entity is actually removed. If any AbortIf condition is true, the entire delete operation (including additional entities) is cancelled.
Syntax
Preventing deletion
productUom.OnDeleting()
.AbortIf((entity, args) => /* condition */)
.WithMessage("Reason deletion is not allowed");
Deleting additional entities
product.OnDeleting()
.AlsoDelete((entity, args) => /* return list of entities to delete */);
Fluent API
| Method | Description |
|---|---|
.AbortIf(condition) |
Cancel deletion when the condition is true |
.WithMessage(message) |
Error message shown when deletion is aborted |
.AlsoDelete(expression) |
Return additional entities to delete with this entity |
AbortIf overloads
| Signature | Description |
|---|---|
.AbortIf((entity, args) => bool) |
Condition with access to event args |
AlsoDelete overloads
| Signature | Description |
|---|---|
.AlsoDelete(entity => IEnumerable) |
Return entities to also delete |
.AlsoDelete((entity, args) => IEnumerable) |
Return entities with access to event args |
Scenarios
1. Preventing deletion of a main UOM
The main unit of measure on a product should not be deletable directly. It can only be deleted when the product itself is deleted.
[Logic]
public class ProductUomBL(ProductUom.Logic productUom)
{
[RegisterLogic]
public void PreventMainUomDeletion()
{
productUom.OnDeleting()
.AbortIf((uom, args) =>
uom.Operation == UomOperation.Main
&& args.OriginalTriggeringEntity != uom.Product)
.WithMessage("Main UOM can not be deleted");
}
}
args.OriginalTriggeringEntitylets you distinguish between a direct delete of the UOM versus a cascading delete triggered by the parent product.
2. Deleting a product-specific pricing schema
When a product is deleted, its pricing schema should also be deleted — but only if it is a schema that is specific to this one product.
[Logic]
public class ProductBL(Product.Logic product)
{
[RegisterLogic]
public void DeleteProductSchema()
{
product.OnDeleting().AlsoDelete(p =>
{
if (p.PricingSchema?.SchemaType == SchemaType.SpecificForOneProduct)
return [p.PricingSchema];
return [];
});
}
}
3. Preventing deletion of a customer with open orders
A customer cannot be deleted if they have any open sales orders.
[Logic]
public class CustomerBL(Customer.Logic customer)
{
[RegisterLogic]
public void PreventDeletionWithOpenOrders()
{
customer.OnDeleting()
.AbortIf((c, args) =>
args.GetEntities<SalesOrder>()
.Any(o => o.SellToCustomer == c
&& o.Status == SalesOrderStatus.Open))
.WithMessage("Cannot delete a customer with open sales orders");
}
}
4. Preventing deletion of a product used on sales orders
A product should not be deletable if it appears on any sales order detail.
[Logic]
public class ProductBL(Product.Logic product)
{
[RegisterLogic]
public void PreventDeletionIfOnOrders()
{
product.OnDeleting()
.AbortIf((p, args) =>
args.GetEntities<SalesOrderDetail>().Any(d => d.Product == p))
.WithMessage("Cannot delete a product that is used on sales orders");
}
}
5. Conditionally deleting related child records
When a sales order is deleted, also delete any draft shipments that have not been sent.
[Logic]
public class SalesOrderBL(SalesOrder.Logic salesOrder)
{
[RegisterLogic]
public void DeleteDraftShipments()
{
salesOrder.OnDeleting().AlsoDelete(o =>
{
return o.Shipments
.Where(s => s.Status == ShipmentStatus.Draft)
.ToList();
});
}
}
Deleting vs. ReferenceProperty DeleteAction
For simple cascading or restricting deletes that don't require custom logic, use the DeleteAction on the [ReferenceProperty] attribute in the model instead:
| DeleteAction | Behavior |
|---|---|
Cascade |
Automatically delete child entities (e.g., SalesOrderDetail when SalesOrder is deleted) |
Restrict |
Prevent deletion of the referenced entity (e.g., can't delete a Product used on a SalesOrderDetail) |
SetNull |
Set the reference to null (e.g., clearing a UOM reference when the UOM is deleted) |
Use OnDeleting() only when you need custom conditional logic beyond what DeleteAction provides.
Notes
- If any
AbortIfcondition is true, the entire delete operation is aborted — including anyAlsoDeleteentities. AlsoDeleteentities go through their own Deleting event pipeline, so they can also abort.- Use
args.OriginalTriggeringEntityto distinguish direct deletes from cascading deletes.