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

如何在EF Core自定义Include方法中为符合条件的集合选择器动态添加Where过滤

如何在EF Core自定义Include方法中为符合条件的集合选择器动态添加Where过滤

我完全理解你的需求:想要做一个通用的Include扩展方法,当操作的主实体和要Include的集合都实现了IObjectWithSecurity接口时,自动给这个集合加上Where(a => a.AllowAccess)的权限过滤,不用每次手动写重复的条件。这个场景在统一权限控制的系统里特别实用。

首先得抓住核心问题:我们需要动态修改传入的propertySelector表达式树——把原来单纯“获取集合”的逻辑(比如i => i.Documents),转换成“获取集合并自动过滤权限”的逻辑(i => i.Documents.Where(a => a.AllowAccess)),再把新的表达式传给EF的Include方法。

解决方案步骤解析

要实现这个目标,我们需要手动构造表达式树,具体分为这几步:

  1. 先构造固定的权限过滤表达式:a => a.AllowAccess,这是我们要追加的统一过滤逻辑。
  2. 从原propertySelector中提取出“获取集合”的表达式节点(比如i.Documents对应的表达式部分)。
  3. 构造调用Queryable.Where方法的表达式,把权限过滤条件绑定到集合上。
  4. 把这个“集合+过滤”的新表达式作为Lambda的Body,用原参数构造出新的propertySelector。
  5. 最后用新的propertySelector调用EF的Include方法返回结果。

完整实现代码

下面是调整后的完整扩展方法代码,我已经处理了类型转换、表达式树构造的细节,并且补上了缺失的分支逻辑:

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Microsoft.EntityFrameworkCore;

public static class QueryableSecurityExtensions
{
    public static IIncludableQueryable<TRepositoryType, TThisInclude> IncludeCheckingSecurity<TRepositoryType, TThisInclude>(
        this IQueryable<TRepositoryType> source,
        Expression<Func<TRepositoryType, TThisInclude>> propertySelector)
        where TRepositoryType : class
    {
        var repoType = typeof(TRepositoryType);
        var includeType = typeof(TThisInclude);

        // 校验主实体和要Include的集合是否都符合权限过滤的条件
        if (typeof(IObjectWithSecurity).IsAssignableFrom(repoType) && 
            typeof(IEnumerable<IObjectWithSecurity>).IsAssignableFrom(includeType))
        {
            // 1. 构造权限过滤条件:s => s.AllowAccess
            var securityParam = Expression.Parameter(typeof(IObjectWithSecurity), "s");
            var allowAccessProp = Expression.Property(securityParam, nameof(IObjectWithSecurity.AllowAccess));
            var filterExpr = Expression.Lambda<Func<IObjectWithSecurity, bool>>(allowAccessProp, securityParam);

            // 2. 取出原表达式中获取集合的部分(比如 i => i.Documents 里的 i.Documents)
            var collectionAccess = propertySelector.Body;

            // 3. 获取Queryable.Where的泛型方法(确保EF能正确翻译成SQL)
            var whereMethod = typeof(Queryable)
                .GetMethods(BindingFlags.Public | BindingFlags.Static)
                .First(m => m.Name == nameof(Queryable.Where) && m.GetParameters().Length == 2)
                .MakeGenericMethod(typeof(IObjectWithSecurity));

            // 4. 构造调用Where后的集合表达式,并转换为目标类型避免类型不匹配
            var filteredCollection = Expression.Convert(
                Expression.Call(whereMethod, collectionAccess, filterExpr),
                includeType);

            // 5. 构造新的Lambda表达式,替换原有的propertySelector
            var newPropertySelector = Expression.Lambda<Func<TRepositoryType, TThisInclude>>(
                filteredCollection,
                propertySelector.Parameters);

            // 6. 用新的表达式调用Include返回结果
            return source.Include(newPropertySelector);
        }
        else
        {
            // 不满足权限过滤条件时,直接返回普通的Include结果
            return source.Include(propertySelector);
        }
    }
}

// 你的权限接口定义(保持不变)
public interface IObjectWithSecurity 
{
    public bool AllowAccess { get; set; }
}

效果验证

当你调用:

var item = await _itemRepository.GetAll()
                                .IncludeCheckingSecurity(i => i.Documents)
                                .SingleAsync();

这个方法会自动帮你转换成等价于:

var item = await _itemRepository.GetAll()
                                .Include(i => i.Documents.Where(a => a.AllowAccess))
                                .SingleAsync();

的查询,EF Core会把这个过滤条件翻译成SQL,只返回AllowAccess为true的Documents集合。

注意事项

  1. EF Core版本要求:这个功能依赖EF Core 5.0及以上版本,因为EF Core 5才正式支持在Include的Lambda中直接过滤集合。
  2. 类型兼容性:代码中加入了Expression.Convert确保返回类型匹配原有的TThisInclude,避免因为集合类型(比如DbSet<T>和IQueryable<T>)的差异导致的类型错误。
  3. 逻辑扩展性:如果后续需要修改权限过滤逻辑,只需要调整filterExpr的构造部分即可,不用修改所有调用的地方。

备注:内容来源于stack exchange,提问作者sjones

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.15 09:03:07