如何在Hot Chocolate中用Connection实现偏移分页?
在Hot Chocolate中用Connection实现偏移分页
核心逻辑
官方文档提到Connection的cursor是不透明的,因此可以将偏移量(offset)编码到cursor中,或者直接在Resolver中处理offset参数,最终包装成符合规范的Connection结构。下面是两种实用实现方式:
方式1:直接接收offset/limit参数
这种方式更直观,适合习惯偏移分页的场景:
1. 定义实体与Repository(示例)
public class Product { public int Id { get; set; } public string Name { get; set; } } public interface IProductRepository { Task<int> GetTotalCountAsync(CancellationToken ct); Task<List<Product>> GetProductsAsync(int offset, int limit, CancellationToken ct); } // EF Core实现示例 public class EfProductRepository : IProductRepository { private readonly AppDbContext _dbContext; public EfProductRepository(AppDbContext dbContext) => _dbContext = dbContext; public async Task<int> GetTotalCountAsync(CancellationToken ct) => await _dbContext.Products.CountAsync(ct); public async Task<List<Product>> GetProductsAsync(int offset, int limit, CancellationToken ct) => await _dbContext.Products.Skip(offset).Take(limit).ToListAsync(ct); }
2. 编写Query Resolver
public class Query { public async Task<Connection<Product>> GetProductsAsync( int offset = 0, int limit = 10, [Service] IProductRepository repository, CancellationToken cancellationToken) { var totalCount = await repository.GetTotalCountAsync(cancellationToken); var products = await repository.GetProductsAsync(offset, limit, cancellationToken); // 为每个商品生成cursor:用偏移量+索引编码 var edges = products.Select((product, index) => new Edge<Product>( product, CursorSerializer.ToCursor(offset + index) )).ToList(); // 判断分页状态 var hasPreviousPage = offset > 0; var hasNextPage = offset + limit < totalCount; // 构造Connection返回 return new Connection<Product>( edges, new PageInfo( hasPreviousPage, hasNextPage, edges.FirstOrDefault()?.Cursor, edges.LastOrDefault()?.Cursor ), totalCount ); } }
方式2:使用标准ConnectionArguments(推荐)
这种方式遵循GraphQL Connection规范,用first(每页数量)和after(上一页最后一个cursor)参数,将cursor解析为偏移量,方便后续切换到游标分页:
编写Resolver
public class Query { public async Task<Connection<Product>> GetProductsAsync( ConnectionArguments args, [Service] IProductRepository repository, CancellationToken cancellationToken) { // 解析after cursor得到偏移量 int offset = args.After != null ? CursorSerializer.FromCursor<int>(args.After) + 1 : 0; int limit = args.First ?? 10; // 后续逻辑与方式1一致 var totalCount = await repository.GetTotalCountAsync(cancellationToken); var products = await repository.GetProductsAsync(offset, limit, cancellationToken); var edges = products.Select((product, index) => new Edge<Product>( product, CursorSerializer.ToCursor(offset + index) )).ToList(); var hasPreviousPage = offset > 0; var hasNextPage = offset + limit < totalCount; return new Connection<Product>( edges, new PageInfo( hasPreviousPage, hasNextPage, edges.FirstOrDefault()?.Cursor, edges.LastOrDefault()?.Cursor ), totalCount ); } }
服务注册
确保在Startup/Program.cs中注册GraphQL服务并启用Connection支持:
builder.Services.AddScoped<IProductRepository, EfProductRepository>(); builder.Services .AddGraphQLServer() .AddQueryType<Query>() .AddType<ProductType>() // 若自定义Product的GraphQL类型则添加 .AddConnectionType<ProductConnection>(); // 自动生成Product的Connection类型
关键说明
CursorSerializer是Hot Chocolate内置工具,负责将整数偏移量编码/解码为符合规范的不透明base64 cursor。- 分页状态判断:
hasPreviousPage基于offset是否大于0,hasNextPage基于当前页结束位置是否小于总数量。 - 若不需要总数量,可省略
totalCount参数(Connection构造函数支持可选总数)。
内容的提问来源于stack exchange,提问作者van9petryk
相关产品推荐
相关产品推荐

