I have a simple Graphql query like this
public class Query
{
public async Task<IPagedList<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"
}
}
};
var ret= await data.ToPagedListAsync(pageIndex, pageSize, data.Count);
return ret;
}
}
Definition for PagedList:
public interface IPagedList<T> : IList<T>
{
int PageIndex { get; }
int PageSize { get; }
int TotalCount { get; }
int TotalPages { get; }
bool HasPreviousPage { get; }
bool HasNextPage { get; }
}
public class PagedList<T> : List<T>, IPagedList<T>
{
public PagedList(IList<T> source, int pageIndex, int pageSize, int? totalCount = null)
{
//min allowed page size is 1
pageSize = Math.Max(pageSize, 1);
TotalCount = totalCount ?? source.Count;
TotalPages = TotalCount / pageSize;
if (TotalCount % pageSize > 0)
TotalPages++;
PageSize = pageSize;
PageIndex = pageIndex;
AddRange(totalCount != null ? source : source.Skip(pageIndex * pageSize).Take(pageSize));
}
public int PageIndex { get; private set; }
public int PageSize { get; private set; }
public int TotalCount { get; private set; }
public int TotalPages { get; private set; }
public bool HasPreviousPage => PageIndex > 0;
public bool HasNextPage => PageIndex + 1 < TotalPages;
}
Static class to call ToPagedListAsync:
public static async Task<IPagedList<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);
//min allowed page size is 1
pageSize = Math.Max(pageSize, 1);
var data = new List<T>();
data.AddRange(await source.Skip(pageIndex * pageSize)
.Take(pageSize)
.ToAsyncEnumerable()
.ToListAsync());
return new PagedList<T>(data, pageIndex, pageSize, totalCount);
}
The result returns the subset of the datasource. I just wonder why through Graphql I cannot get access to PagedList members (pageIndex, pageSize, totalCount, hasPreviousPage...) . I know I can refactor the method to return new model, but that's extra work.
Well, I guess, it is not implemented that way because the idea is quite ambiguous and controversial. I.e. not all developers of graphql APIs would support that.
As an example, suppose, it's going to be implemented. But making a query to the books collection you define the collection item fields selection but not the collection properties selection. That approach now needs to be changed - to request the collection properties an additional level of hierarchy has to be introduced:
Would everybody be happy when automatically getting the new level of hierarchy (i.e. items)? I'm not sure. Okay, suppose, that new level is introduced. But which properties of the interfaces or classes have to be automatically exposed, given that many of them might be just for the programming usage but not for the API? Consider the easiest case - IList. Should its property IsReadOnly be exposed? Obviously, it does make sense for neither API implementation. So, even if the idea is worthwhile at first glance, in reality, its out-of-the-box implementation would unlikely satisfy anybody.
Regarding your particular case that might be worthwhile to consider Offset Pagination - it is out-of-the-box support of the paged lists (the functionality you are trying to achieve with PagedList<T>).