You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 21:33:15