如何编写在UseFiltering之后执行的HotChocolate中间件?
问题:在HotChocolate v13中编写获取过滤后IQueryable的字段中间件
基于现有.NET WebAPI使用HotChocolate v13构建GraphQL接口时,为带有UseFiltering()的字段编写自定义中间件,在调用await next(context)前后断点查看context.Result,发现始终是未应用过滤的原始IQueryable<Product>,无法获取客户端过滤后的结果。
代码示例:
public class QueryType : ObjectType<Query> { protected override void Configure(IObjectTypeDescriptor<GraphQuery> descriptor) { descriptor.Authorize(); descriptor .Field(x => x.GetProducts(default!, default!)) .UseFiltering() .Use(next => async context => { // 断点:context.Result 为 null await next(context); // 断点:context.Result 是未过滤的 AppDbContext.Products }) .Type<ListType<ProductType>>(); base.Configure(descriptor); } } public class Query { public IQueryable<Product> GetProducts([Service] AppDbContext dbContext, [ScopedService] ICurrentUser currentUser) { return dbContext.Products; } }
解决方案
核心问题是中间件的注册顺序:HotChocolate的中间件按注册顺序链式执行,你当前把自定义中间件放在UseFiltering()之后,导致过滤逻辑还未执行就进入了自定义中间件,调用next(context)才会触发过滤,但此时拿到的仍是原始IQueryable(过滤逻辑是对IQueryable做表达式包装,而非立即执行查询)。
要获取过滤后的IQueryable,需将自定义中间件注册在UseFiltering()之前:
public class QueryType : ObjectType<Query> { protected override void Configure(IObjectTypeDescriptor<GraphQuery> descriptor) { descriptor.Authorize(); descriptor .Field(x => x.GetProducts(default!, default!)) // 先注册自定义中间件 .Use(next => async context => { await next(context); // 此时 context.Result 就是应用了客户端过滤条件的 IQueryable<Product> var filteredProducts = context.Result as IQueryable<Product>; // 可在此对过滤后的IQueryable做额外处理,比如追加自定义过滤 // context.Result = filteredProducts.Where(p => p.IsActive); }) .UseFiltering() .Type<ListType<ProductType>>(); base.Configure(descriptor); } }
原理说明
- 自定义中间件先注册,执行时先进入其逻辑,调用
await next(context)会将执行权传递给后续的UseFiltering()中间件。 UseFiltering()中间件解析客户端过滤参数,将过滤表达式应用到原始IQueryable<Product>上,生成带过滤条件的新IQueryable并赋值给context.Result。- 执行权回到自定义中间件,此时
context.Result就是已应用过滤的IQueryable,可在此进行后续操作。
注意:IQueryable是延迟执行的,此时看到的context.Result是带有过滤表达式的查询对象,数据库查询会在后续执行环节才真正触发。
内容的提问来源于stack exchange,提问作者Alberto Cruz
相关产品推荐
相关产品推荐

