ef core全局查询过滤器必须在onmodelcreating中用hasqueryfilter注册且仅生效一次,典型软删除写法为modelbuilder.entity().hasqueryfilter(x => !x.isdeleted),支持find、where、include等查询,绕过需用ignorequeryfilters()。

EF Core 全局查询过滤器怎么加
全局过滤器必须在 OnModelCreating 中通过 modelBuilder.Entity<t>().HasQueryFilter()</t> 注册,且只能注册一次。重复调用会覆盖前一个,不会合并。
软删除场景下,典型写法是:
modelBuilder.Entity<user>().HasQueryFilter(x => !x.IsDeleted);</user>
注意:IsDeleted 字段必须是实体类的属性(不能是计算字段或导航属性),且类型为 bool 或可空 bool?;若为 bool?,需显式处理 null(如 x.IsDeleted != true)。
- 过滤器对所有查询生效——包括
FindAsync、FirstOrDefault、Include关联查询(但不自动穿透到被 Include 的子实体,除非子实体也单独配置了过滤器) - 迁移生成时,EF Core 不会因过滤器自动加索引,建议手动为
IsDeleted加索引提升性能 - 若实体有多个软删除状态(如
Deleted/Archived),不能靠一个布尔字段表达,得换用枚举 + 更复杂的表达式,但会增加过滤器维护成本
如何临时绕过全局过滤器查已删除数据
用 IgnoreQueryFilters() 方法可以跳过当前查询的全局过滤逻辑,仅对本次查询生效,不影响其他地方。
常见于后台管理「回收站」页面或数据修复脚本:
var deletedUsers = await context.Users.IgnoreQueryFilters()
.Where(x => x.IsDeleted)
.ToListAsync();
- 它只作用于调用它的
IQueryable链,不会影响后续新建的查询 - 不能在
AsNoTracking()之后再调用IgnoreQueryFilters()——顺序错误会抛InvalidOperationException - 若用了自定义仓储模式,别把
IgnoreQueryFilters()封装进通用 Get 方法里,否则容易误开后门;应单独暴露如GetAllIncludingDeleted()这类明确语义的方法
IsDeleted 字段没生效?检查这三处
最常见失效原因不是代码写错,而是模型配置和运行时行为不匹配。
-
IsDeleted属性未映射到数据库列:确认实体类中该属性有对应字段,且未被[NotMapped]标记;若用 Fluent API 映射,要确保没漏掉Property(x => x.IsDeleted) - 实体继承体系中父类配置了过滤器,但子类没调用
base.OnModelCreating()——子类OnModelCreating必须显式调用基类方法,否则父类过滤器不生效 - 使用了
AsSplitQuery()或多表 JOIN 查询时,EF Core 8+ 才支持将全局过滤器正确应用到所有参与表;旧版本(如 EF Core 6)可能只过滤主表,导致关联数据“漏出”已删除项
软删除与并发更新冲突怎么处理
当业务同时涉及「逻辑删除」和「状态变更」(如用户禁用 + 删除),直接执行 Update 可能意外激活已删除记录。
推荐做法是:所有修改操作都显式检查 IsDeleted == false,例如:
var user = await context.Users.FirstOrDefaultAsync(x => x.Id == id && !x.IsDeleted);
而不是先查再判断——避免中间状态被并发修改。
- 不要依赖
Attach+Entry.State = Modified更新软删除实体,这会绕过查询过滤,可能更新到已删除行 - 若必须批量恢复(如“还原选中用户”),用原始 SQL 或
ExecuteUpdateAsync(EF Core 7+)并显式加WHERE IsDeleted = 1条件,避免全表扫描 - 数据库层面建议给
IsDeleted列加约束(如CHECK (IsDeleted IN (0,1))),防止数据异常污染过滤逻辑
全局过滤器看着简单,但一旦嵌套在继承、多上下文、读写分离或 CQRS 架构里,HasQueryFilter 的作用域和组合行为就很容易出人意料。尤其要注意它不参与 EF Core 的变更跟踪判定——被过滤掉的实体不会进入 ChangeTracker,这点常被忽略。











