Entity Framework 6泛型仓储如何过滤关联实体的软删除状态
可以在泛型仓储中统一过滤Include的软删除实体吗?
当然可以实现!而且有几种实用的方案,能帮你在泛型仓储层统一处理关联实体的软删除过滤,不用在每个具体仓储的查询里重复写检查逻辑。下面我给你详细讲讲:
方案一:EF Core 全局查询筛选器(最推荐)
这是最省心的方案——EF Core提供的全局查询筛选器会自动将软删除过滤逻辑应用到所有查询,包括Include、ThenInclude关联的实体,甚至懒加载的导航属性。
实现步骤:
在你的DbContext的OnModelCreating方法中,给所有继承自BaseEntity的实体添加统一的筛选规则:
protected override void OnModelCreating(ModelBuilder modelBuilder) { // 遍历所有继承自BaseEntity的实体类型 foreach (var entityType in modelBuilder.Model.GetEntityTypes() .Where(t => typeof(BaseEntity).IsAssignableFrom(t.ClrType))) { // 构建 "实体.IsDeleted == false" 的筛选表达式 var parameter = Expression.Parameter(entityType.ClrType, "e"); var filterExpression = Expression.Lambda( Expression.Equal( Expression.Property(parameter, nameof(BaseEntity.IsDeleted)), Expression.Constant(false)), parameter); // 给当前实体添加全局查询筛选器 modelBuilder.Entity(entityType.ClrType).HasQueryFilter(filterExpression); } // 其他实体配置逻辑... }
为什么推荐这个方案?
- 完全自动化:不管是主实体查询,还是关联实体的
Include,都会自动过滤掉IsDeleted = true的记录,你甚至不需要修改现有仓储的Get方法! - 避免重复代码:不用在每个仓储、每个查询里手动加软删除过滤条件。
- 支持所有查询场景:包括懒加载、显式加载、
ThenInclude多层关联等。
例外情况处理:
如果偶尔需要查询已删除的实体,只需要在查询时调用IgnoreQueryFilters()即可:
var deletedEntities = dbContext.YourEntities.IgnoreQueryFilters().Where(e => e.IsDeleted).ToList();
方案二:在泛型仓储的Include逻辑中手动过滤(适合无法用全局筛选器的场景)
如果因为某些特殊需求不能使用全局筛选器(比如部分实体不需要软删除过滤),可以在仓储的Get方法中手动处理Include的关联实体过滤。
修改后的仓储Get方法:
public abstract class Repository<T, TOrderBy> where T : BaseEntity { // 假设Set是DbSet<T>的实例,需要在具体仓储中初始化 protected abstract DbSet<T> Set { get; } protected IQueryable<T> Get(Expression<Func<T, bool>> filter = null, params Expression<Func<T, object>>[] includes) { var query = Set.AsQueryable(); // 先过滤主实体的软删除 query = query.Where(x => !x.IsDeleted); if (filter != null) { query = query.Where(filter); } if (includes != null) { foreach (var include in includes) { var includeBody = include.Body as MemberExpression; if (includeBody == null) { query = query.Include(include); continue; } var navigationType = includeBody.Type; // 处理集合类型的导航属性(比如ICollection<U>) if (navigationType.IsGenericType && typeof(ICollection<>).IsAssignableFrom(navigationType.GetGenericTypeDefinition())) { var relatedEntityType = navigationType.GetGenericArguments()[0]; if (typeof(BaseEntity).IsAssignableFrom(relatedEntityType)) { // 构建关联实体的软删除过滤表达式 var param = Expression.Parameter(relatedEntityType, "u"); var filterExpr = Expression.Lambda( Expression.Equal( Expression.Property(param, nameof(BaseEntity.IsDeleted)), Expression.Constant(false)), param); // 调用EF的Include+Where重载,过滤集合中的已删除实体 var includeMethod = typeof(EntityFrameworkQueryableExtensions) .GetMethods() .First(m => m.Name == "Include" && m.GetParameters().Length == 2 && m.GetParameters()[1].ParameterType.GetGenericTypeDefinition() == typeof(Func<,>)) .MakeGenericMethod(typeof(T), relatedEntityType); var filteredIncludeExpr = Expression.Lambda( Expression.Call( typeof(Enumerable), "Where", new[] { relatedEntityType }, includeBody, filterExpr), include.Parameters); query = (IQueryable<T>)includeMethod.Invoke(null, new object[] { query, filteredIncludeExpr }); continue; } } // 处理引用类型的导航属性(比如一对一关联) else if (typeof(BaseEntity).IsAssignableFrom(navigationType)) { query = query.Include(include); // 添加引用实体的软删除过滤条件 var param = Expression.Parameter(typeof(T), "t"); var navigationProp = Expression.Property(param, includeBody.Member.Name); var isDeletedProp = Expression.Property(navigationProp, nameof(BaseEntity.IsDeleted)); var referenceFilter = Expression.Lambda<Func<T, bool>>( Expression.Equal(isDeletedProp, Expression.Constant(false)), param); query = query.Where(referenceFilter); continue; } // 非BaseEntity的关联实体,直接普通Include query = query.Include(include); } } return query; } }
注意事项:
- 这个方案需要处理两种导航属性:集合类型(一对多)和引用类型(一对一),逻辑相对复杂。
- 对于多层
ThenInclude的场景,这个代码还需要扩展,处理起来会更繁琐。 - 相比全局筛选器,这种方式更容易出现遗漏,比如忘记处理某些导航类型。
总结
如果你的项目基于EF Core,强烈推荐使用全局查询筛选器,它是最简单、最可靠的统一软删除过滤方案。只有当你有特殊业务需求,无法全局应用筛选器时,再考虑在泛型仓储中手动处理Include的过滤逻辑。
内容的提问来源于stack exchange,提问作者sixfeet
相关产品推荐
相关产品推荐

