如何在.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
相关产品推荐
相关产品推荐

