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

如何在.NET 6的EF Core中实现动态全局查询过滤器?

解决方案

1. 注册HttpContext访问服务

在Program.cs中添加IHttpContextAccessor的注册,让DbContext能访问当前请求的上下文:

builder.Services.AddHttpContextAccessor();
builder.Services.AddDbContext<YourDbContext>(options => 
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

2. 修改DbContext构造函数,注入IHttpContextAccessor

替换原来直接注入_companyId的方式,改为注入IHttpContextAccessor,用于动态获取每个请求的CompanyId:

public class YourDbContext : DbContext
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public YourDbContext(DbContextOptions<YourDbContext> options, IHttpContextAccessor httpContextAccessor)
        : base(options)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    // 新增方法:获取当前请求的CompanyId
    public long? GetCurrentCompanyId()
    {
        // 根据实际认证逻辑调整,比如从Claims/HttpContext.Items中读取
        if (_httpContextAccessor.HttpContext?.User?.FindFirst("CompanyId") is Claim companyIdClaim 
            && long.TryParse(companyIdClaim.Value, out var companyId))
        {
            return companyId;
        }
        return null;
    }

    // 你的DbSet定义...
}

3. 重构全局查询过滤器实现

由于OnModelCreating仅启动时执行一次,不能直接使用固定变量,需要通过表达式树调用DbContext的GetCurrentCompanyId方法,确保每次查询都能获取当前请求的CompanyId:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    // 通用软删除过滤
    modelBuilder.ApplyQueryFilters<IDeletedAt>(x => x.DeletedAt == null);

    // 针对实现IDeletedAtAndCompany接口的实体,添加CompanyId动态过滤
    foreach (var entityType in modelBuilder.Model.GetEntityTypes())
    {
        if (!typeof(IDeletedAtAndCompany).IsAssignableFrom(entityType.ClrType))
            continue;

        var parameter = Expression.Parameter(entityType.ClrType, "x");
        
        // 构建DeletedAt == null的条件
        var deletedAtProperty = Expression.Property(parameter, nameof(IDeletedAtAndCompany.DeletedAt));
        var deletedAtCondition = Expression.Equal(deletedAtProperty, Expression.Constant(null, typeof(DateTime?)));
        
        // 构建CompanyId匹配当前请求ID的条件
        var getCompanyIdMethod = typeof(YourDbContext).GetMethod(nameof(GetCurrentCompanyId), 
            BindingFlags.Public | BindingFlags.Instance);
        var companyIdCall = Expression.Call(Expression.Constant(this), getCompanyIdMethod);
        var companyIdProperty = Expression.Property(parameter, nameof(IDeletedAtAndCompany.CompanyId));
        var companyIdCondition = Expression.Equal(companyIdProperty, companyIdCall);
        
        // 组合两个过滤条件
        var combinedFilter = Expression.AndAlso(deletedAtCondition, companyIdCondition);
        var filterLambda = Expression.Lambda(combinedFilter, parameter);

        modelBuilder.Entity(entityType.ClrType).HasQueryFilter(filterLambda);
    }
}

关键说明

  • GetCurrentCompanyId会在每次查询执行时被调用,确保获取的是当前请求的CompanyId,而非启动时的固定值。
  • 请根据你的实际认证方式调整GetCurrentCompanyId内的逻辑(比如从JWT Claims、请求头或其他存储位置读取CompanyId)。
  • DbContext默认是Scoped生命周期,每个请求会创建一个实例,因此_httpContextAccessor能正确绑定当前请求的上下文。

内容的提问来源于stack exchange,提问作者Vahid Moshtaghian

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 11:50:20