EF Core 7如何编写Include扩展方法过滤未删除关联集合
实现EF Core 7带软删除过滤的Include扩展方法
完整扩展方法实现
结合你的需求,我们可以通过表达式树拼接来实现自动合并过滤逻辑的扩展方法,代码如下:
using System.Linq.Expressions; using Microsoft.EntityFrameworkCore; public static class QueryableExtensions { public static IIncludableQueryable<TEntity, TCollection> Include<TEntity, TElement, TCollection>( this IQueryable<TEntity> query, Expression<Func<TEntity, TCollection>> navigationExpression, bool withoutDeleted) where TEntity : class, IDbEntityDelete where TElement : class, IDbEntityDelete where TCollection : IEnumerable<TElement> { // 不需要过滤时直接返回原生Include结果 if (!withoutDeleted) { return query.Include(navigationExpression); } // 获取导航属性的访问表达式(例如x => x.Roles的主体是x.Roles) var navigationAccess = navigationExpression.Body; // 反射获取Enumerable.Where的泛型方法定义 var whereMethod = typeof(Enumerable) .GetMethods() .First(m => m.Name == nameof(Enumerable.Where) && m.GetParameters().Length == 2) .MakeGenericMethod(typeof(TElement)); // 创建集合元素的参数(对应Where中的y) var elementParam = Expression.Parameter(typeof(TElement), "y"); // 构建软删除过滤表达式:y => !y.IsDeleted var filterExpression = Expression.Lambda<Func<TElement, bool>>( Expression.Not(Expression.Property(elementParam, nameof(IDbEntityDelete.IsDeleted))), elementParam); // 拼接Where调用表达式:x.Roles.Where(y => !y.IsDeleted) var whereCall = Expression.Call( instance: null, // Enumerable方法是静态方法,无需实例 method: whereMethod, arguments: new[] { navigationAccess, filterExpression }); // 生成最终的导航Lambda表达式:x => x.Roles.Where(...) var newNavigationExpr = Expression.Lambda<Func<TEntity, TCollection>>( whereCall, navigationExpression.Parameters); // 调用EF Core原生Include方法 return query.Include(newNavigationExpr); } } // 软删除接口定义(需与你的项目实现一致) public interface IDbEntityDelete { bool IsDeleted { get; set; } }
关键逻辑解析
- 泛型约束适配:新增
TElement泛型参数并绑定IDbEntityDelete,同时限定TCollection为IEnumerable<TElement>,确保我们能对集合元素应用过滤逻辑。 - 表达式树拼接:通过
Expression.Call拼接Where方法调用,将导航属性访问逻辑和软删除过滤逻辑组合成完整的表达式。 - 参数一致性处理:通过
Expression.Parameter创建集合元素的参数,替换过滤逻辑中的参数,保证表达式树的参数上下文一致。
使用示例
// 自动过滤已删除的Role数据 var users = _context.Users.Include<User, Role, ICollection<Role>>(x => x.Roles, true).ToList(); // 不启用过滤,加载所有Role数据 var usersWithAllRoles = _context.Users.Include<User, Role, ICollection<Role>>(x => x.Roles, false).ToList();
优化简化调用(可选)
如果想进一步减少泛型参数书写,可以调整方法名并设置默认参数,让类型自动推导:
public static IIncludableQueryable<TEntity, TCollection> IncludeWithSoftDelete<TEntity, TElement, TCollection>( this IQueryable<TEntity> query, Expression<Func<TEntity, TCollection>> navigationExpression, bool withoutDeleted = true) where TEntity : class, IDbEntityDelete where TElement : class, IDbEntityDelete where TCollection : IEnumerable<TElement> { // 内部逻辑与上述方法一致 } // 简化调用 var users = _context.Users.IncludeWithSoftDelete(x => x.Roles).ToList();
内容的提问来源于stack exchange,提问作者wh1sp3r
相关产品推荐
相关产品推荐

