EF Core级联删除需模型配置、迁移生成和数据库外键三者同时满足ON DELETE CASCADE才生效,缺一不可;否则会报约束冲突或行为异常。

EF Core 的级联删除不会自动生效,必须同时满足模型配置和数据库外键约束两个条件,缺一不可。
OnDelete(DeleteBehavior.Cascade) 必须配合非空外键
只有当子实体的外键属性(如 ParentId)是不可为空类型(int、Guid 等)时,OnDelete(DeleteBehavior.Cascade) 才合法。可空外键(如 int?)不能配 Cascade,否则迁移会失败或运行时报错。
- 必选关系(Required)才允许
Cascade:EF Core 默认只对这种关系启用级联逻辑 - 可选关系应改用
SetNull或软删除,否则配置会被忽略 - 导航属性名必须与子实体中真实属性名完全一致,比如
WithOne(c => c.Parent)中的c.Parent要真有这个属性,拼错就失效
数据库外键必须含 ON DELETE CASCADE
仅在 OnModelCreating 里写 OnDelete(DeleteBehavior.Cascade) 不够——EF Core 迁移生成的 SQL 外键定义里,必须实际包含 ON DELETE CASCADE,否则数据库层不执行级联。
- 如果跳过
dotnet ef migrations add和update-database,旧库结构不会自动更新 - 手动建库或用脚本初始化时,得自己补上
ON DELETE CASCADE,否则 EF Core 删除父实体会直接抛SqlException约束冲突 - 检查已有外键是否生效:SQL Server 可查
sys.foreign_keys表的delete_referential_action_desc字段是否为CASCADE
Remove 后 SaveChanges 的实际行为分三种情况
调用 context.Parents.Remove(parent) 之后的级联效果,取决于父实体是否被上下文跟踪,以及数据库外键是否就位:
- 实体是从 DB 查出且被跟踪的 → EF Core 先加载子实体(如果没
Include则触发额外查询),再发多条DELETE语句 - 实体是 new 出来后直接
Remove的 → EF Core 不知道它有没有子项,也不查,全靠数据库外键兜底执行物理级联 - 实体被跟踪但数据库外键没设
CASCADE→SaveChanges报错:The DELETE statement conflicted with the REFERENCE constraint
生产环境更推荐 ExecuteDelete + 显式顺序控制
纯依赖 Cascade 容易误删、难审计、事务边界模糊。EF Core 7+ 的 ExecuteDelete 是更可控的替代方案:
- 先删子:
context.Children.Where(c => c.ParentId == id).ExecuteDelete(),不加载数据,高效安全 - 再删父:
context.Parents.Remove(parent),此时已无子项,无约束风险 - 整个过程可包在
using var transaction = context.Database.BeginTransaction()中统一控制 - 若需软删除,只需把
ExecuteDelete换成ExecuteUpdate(e => e.SetProperty(x => x.IsDeleted, true))
真正容易被忽略的是:级联删除不是“开了配置就稳了”,而是模型、迁移、数据库三者必须严格对齐;哪怕只漏掉一次迁移更新,线上就可能因外键缺失而崩掉删除操作。











