ef core软删除必须通过hasqueryfilter配置全局查询过滤器并配合拦截savechanges实现逻辑删除。需为实体显式配置isdeleted字段的过滤条件,重写删除行为为更新操作,必要时用ignorequeryfilters绕过过滤,并为isdeleted字段建立索引以保障性能。

EF Core 软删除必须靠 HasQueryFilter 配合字段标记
软删除不是“删数据”,而是让查询自动过滤掉被标记为已删除的记录。EF Core 不提供开箱即用的“软删开关”,核心依赖 HasQueryFilter 在模型配置中注入全局 WHERE 条件。没配这个,哪怕数据库里加了 IsDeleted 字段,查询照样返回所有行。
常见错误现象:context.Users.ToList() 仍查出已“删除”的用户;调用 Remove() 后数据真被物理删了;或者手动写 .Where(x => !x.IsDeleted) 到处都是,漏一处就出问题。
- 必须在
OnModelCreating中为每个需软删除的实体显式配置HasQueryFilter,不能只靠约定 - 推荐统一使用
bool IsDeleted字段(而非DateTime? DeletedAt),避免空值比较引发的 SQL NULL 语义陷阱 - 如果实体有继承关系(如
AuditEntity基类),HasQueryFilter可以写在基类映射里,但子类若重写该属性,需确保类型转换安全
删除操作要重写 SaveChanges 或用拦截器,不能直接 Remove()
EF Core 默认的 Remove() 是物理删除。想实现“点删除变更新”,得拦截保存动作,把 EntityState.Deleted 实体转成 EntityState.Modified 并设置 IsDeleted = true。否则,哪怕配了 HasQueryFilter,数据也早被 DELETE 语句干掉了。
使用场景:管理后台点击“删除用户”,后端 API 接收请求后调用 context.Users.Remove(user) —— 这行代码本身不危险,危险的是紧接着的 SaveChanges()。
- 推荐用
SaveChangesInterceptor(EF Core 5.0+),比重写SaveChanges更干净,避免递归调用风险 - 拦截时只处理状态为
Deleted且类型实现了软删除接口(如IHasSoftDelete)的实体 - 记得清除原
Deleted状态并设为Modified,否则 EF 仍会生成 DELETE 语句 - 注意并发:如果同一实体在拦截前已被其他逻辑标记为
IsDeleted = true,重复设置可能触发无意义更新
IgnoreQueryFilters 是绕过软删除的唯一合法方式,但要用对地方
有些场景确实需要查“全部”,比如回收站页面、后台数据修复、或导出报表。这时不能删 HasQueryFilter,也不能临时改模型配置,唯一正确做法是调用 IgnoreQueryFilters() 方法。
错误用法:context.Users.AsNoTracking().Where(...) 依然受过滤器影响;FromSqlRaw 虽然能绕过,但失去变更跟踪和类型安全,属于退化方案。
-
IgnoreQueryFilters()必须在IQueryable链最前端调用,例如:context.Users.IgnoreQueryFilters().Where(x => x.IsDeleted) - 它只对当前查询生效,不影响后续查询,也不改变上下文状态
- 不要在通用仓储方法里默认加
IgnoreQueryFilters(),否则软删除形同虚设 - 配合
AsNoTracking()使用没问题,但两者目的不同:前者关过滤,后者关跟踪
软删除字段必须建索引,否则查询性能会断崖下跌
HasQueryFilter 最终生成的 SQL 是带 WHERE IsDeleted = 0 的,如果表大、IsDeleted 区分度低(比如 95% 数据都已删除),又没索引,全表扫描就来了。线上查个列表卡 3 秒,八成是这原因。
兼容性影响:SQL Server 和 PostgreSQL 对布尔字段索引支持良好;MySQL 5.7 之前不支持函数索引,IsDeleted 字段单独建 B-Tree 索引即可;SQLite 没问题,但别指望它扛高并发。
- 索引名建议带业务含义,比如
IX_Users_IsDeleted,方便 DBA 识别用途 - 如果经常按
IsDeleted + CreatedAt组合查询(如查最近删除的用户),建联合索引更高效 - 迁移脚本里补索引时,注意生产环境锁表风险,大表优先用
CREATE INDEX CONCURRENTLY(PostgreSQL)或在线索引(SQL Server Enterprise)
软删除看着只是加个字段和一行配置,但查询路径、保存路径、索引策略三者缺一不可。少配一个,轻则数据泄露,重则性能雪崩——尤其当“已删除”数据量反超正常数据时,那个 IsDeleted 字段就从开关变成瓶颈了。











