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