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

如何通过GraphQL访问继承IList的IPagedList成员属性?

问题原因
  • GraphQL的类型系统默认会将继承自IEnumerable<T>/IList<T>的类型识别为列表类型,只会暴露列表中的元素,不会自动解析接口或类中额外的分页属性。
  • 你的Query方法返回的是IPagedList<Book>接口类型,GraphQL默认不会主动扫描接口的成员来生成Schema字段,只会处理它继承的IList<T>的列表行为。
可行解决办法

方法1:修改返回类型为具体类PagedList<T>(最小工作量)

直接把Query方法的返回类型从Task<IPagedList<Book>>改成Task<PagedList<Book>>,同时调整ToPagedListAsync的返回类型,让GraphQL能直接反射具体类的所有公共属性(包括分页成员和列表元素):

修改Query方法

public async Task<PagedList<Book>> Books(int pageIndex, int pageSize)
{
    var data = new List<Book>() {
        new() {
            Title = "C# in depth.",
            Author = new Author { Name = "Jon Skeet" }
        },
        new() {
            Title = "How to cook Phở",
            Author = new Author { Name = "Nam Vo" }
        },
        new() {
            Title = "How to cook Phở2",
            Author = new Author { Name = "Nam Vo" }
        },
        new() {
            Title = "How to cook Phở3",
            Author = new Author { Name = "Nam Vo" }
        }
    };
    return await data.ToPagedListAsync(pageIndex, pageSize, data.Count);
}

修改ToPagedListAsync方法

public static async Task<PagedList<T>> ToPagedListAsync<T>(this IEnumerable<T> source,
                                                      int pageIndex,
                                                      int pageSize,
                                                      int totalCount)
{
    if (source == null)
        return new PagedList<T>(new List<T>(), pageIndex, pageSize);

    pageSize = Math.Max(pageSize, 1);
    var data = await source.Skip(pageIndex * pageSize)
        .Take(pageSize)
        .ToAsyncEnumerable()
        .ToListAsync();

    return new PagedList<T>(data, pageIndex, pageSize, totalCount);
}

方法2:显式配置GraphQL接口类型(适合保留接口返回的场景)

如果不想修改返回类型,可以在GraphQL的Schema配置中,为IPagedList<T>接口显式声明字段。以HotChocolate为例:

编写接口类型扩展

public class IPagedListTypeExtension<T> : ObjectTypeExtension<IPagedList<T>>
{
    protected override void Configure(IObjectTypeDescriptor<IPagedList<T>> descriptor)
    {
        descriptor.Name($"IPagedList{typeof(T).Name}");
        descriptor.Field(x => x.PageIndex).Type<IntType>();
        descriptor.Field(x => x.PageSize).Type<IntType>();
        descriptor.Field(x => x.TotalCount).Type<IntType>();
        descriptor.Field(x => x.TotalPages).Type<IntType>();
        descriptor.Field(x => x.HasPreviousPage).Type<BooleanType>();
        descriptor.Field(x => x.HasNextPage).Type<BooleanType>();
        
        // 暴露列表元素
        descriptor.Field("items")
            .Type<ListType<ObjectType<T>>>()
            .Resolver(ctx => ctx.Parent<IPagedList<T>>());
    }
}

注册扩展类型

builder.Services.AddGraphQLServer()
    .AddQueryType<Query>()
    .AddTypeExtension<IPagedListTypeExtension<Book>>();

方法3:用匿名类型临时包装(兼容现有接口)

如果不想修改现有结构,可在Query方法中将IPagedList<T>包装成匿名类型,让GraphQL自动识别所有属性:

public async Task<object> Books(int pageIndex, int pageSize)
{
    var data = new List<Book>() {
        new() {
            Title = "C# in depth.",
            Author = new Author { Name = "Jon Skeet" }
        },
        new() {
            Title = "How to cook Phở",
            Author = new Author { Name = "Nam Vo" }
        },
        new() {
            Title = "How to cook Phở2",
            Author = new Author { Name = "Nam Vo" }
        },
        new() {
            Title = "How to cook Phở3",
            Author = new Author { Name = "Nam Vo" }
        }
    };
    var pagedList = await data.ToPagedListAsync(pageIndex, pageSize, data.Count);
    
    return new {
        Items = pagedList,
        pagedList.PageIndex,
        pagedList.PageSize,
        pagedList.TotalCount,
        pagedList.TotalPages,
        pagedList.HasPreviousPage,
        pagedList.HasNextPage
    };
}

内容的提问来源于stack exchange,提问作者nam vo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 05:45:37