Введение
В этой статье мы поговорим о пагинации на GraphQL.
Если вы не знаете, что такое graphQL, пожалуйста, ознакомьтесь с нашей статьей.
О конфигурировании и первоначальном использовании graphQL читайте в этой статье.
Одним из наиболее эффективных способов работы с GraphQL является взаимодействие при помощи интерфейса IQueryable
. Наш второй проект в репозитории (002_GraphQLWithEFCore
) посвящен обеспечению взаимодействия GraphQL с EF Core (Entity Framework (EF) Core — это простая, кроссплатформенная и расширяемая версия популярной технологии доступа к данным Entity Framework с открытым исходным кодом).
Сначала давайте введем данные о Customer (клиент) и Card (карта) в базу данных с помощью EF Core.
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public List<Card> Cards { get; set; }
}
public class Card
{
public int Id { get; set; }
public string Number { get; set; }
public string CVC { get; set; }
public string ExpiryDate { get; set; }
public int CustomerId { get; set; }
public Customer Customer { get; set; }
}
Давайте настроим DbContext:
public class GraphQLAppDbContext : DbContext
{
public GraphQLAppDbContext(DbContextOptions<GraphQLAppDbContext> options) : base(options) { }
public DbSet<Customer> Customers { get; set; }
public DbSet<Card> Cards { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Customer>()
.HasMany(e => e.Cards)
.WithOne(x => x.Customer)
.HasForeignKey(x => x.CustomerId)
.IsRequired();
modelBuilder.Entity<Customer>()
.HasData(new Customer() { Id = 1, Email = "id1@gmail.com", Name = "Simon Baker" },
new Customer() { Id = 2, Email = "id2@gmail.com", Name = "Hanayama Kaoru" },
new Customer() { Id = 3, Email = "id3@gmail.com", Name = "Hanma Baki" },
new Customer() { Id = 4, Email = "id4@gmail.com", Name = "O. Doppo" });
modelBuilder.Entity<Card>()
.HasData(new Card() { Id = 1, CustomerId = 1, Number = "1234-2345-3445-3456", CVC = "345", ExpiryDate = "08/26" },
new Card() { Id = 2, CustomerId = 2, Number = "1234-2345-3445-3456", CVC = "345", ExpiryDate = "08/26" },
new Card() { Id = 3, CustomerId = 3, Number = "3344-7654-3445-3456", CVC = "123", ExpiryDate = "09/25" },
new Card() { Id = 4, CustomerId = 4, Number = "5566-5454-3445-3456", CVC = "234", ExpiryDate = "07/24" },
new Card() { Id = 5, CustomerId = 1, Number = "6677-3232-3445-3456", CVC = "987", ExpiryDate = "06/26" },
new Card() { Id = 6, CustomerId = 1, Number = "8899-9898-3445-3456", CVC = "654", ExpiryDate = "05/27" },
new Card() { Id = 7, CustomerId = 2, Number = "9900-5678-3445-3456", CVC = "432", ExpiryDate = "04/28" },
new Card() { Id = 8, CustomerId = 1, Number = "1122-5678-3445-3456", CVC = "165", ExpiryDate = "03/23" },
new Card() { Id = 9, CustomerId = 2, Number = "2233-2543-3445-3456", CVC = "651", ExpiryDate = "02/29" },
new Card() { Id = 10, CustomerId = 3, Number = "3544-1234-3445-3456", CVC = "987", ExpiryDate = "01/26" });
base.OnModelCreating(modelBuilder);
}
}
Далее давайте настроим наш Query resolver (распознаватель запросов). (код)
public class Query
{
public IQueryable<Customer> GetCustomers([Service] GraphQLAppDbContext graphqldbcontext)
=> graphqldbcontext.Customers.Include("Cards");
}
Наконец, давайте настроим hotchocolate, как в первом проекте.
using _002_GraphQLWithEFCore.Database;
using _002_GraphQLWithEFCore.Queries;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<GraphQLAppDbContext>(
options => options.UseSqlServer("Data Source=.;Initial Catalog=GraphQLDb;Integrated Security=SSPI;TrustServerCertificate=True;"));
builder.Services.AddGraphQLServer()
.AddQueryType<Query>();
var app = builder.Build();
app.MapGraphQL();
app.Run();
Теперь, когда мы готовы запустить наш проект, можно получить всю информацию о клиенте и его карте в следующей форме.
Пагинация в GraphQL
Пагинация — это один из основных механизмов, который мы постоянно применяем на практике, и он позволяет получать данные по частям, а не целиком.
Используемые в настоящее время формы пагинации делятся на 2 вида:
Пагинация на основе смещения
Это форма пагинации с комбинацией Skip-Take, которую мы также часто используем в EF Core. Но в системах с большим потоком данных такая форма пагинации не очень удобна. Представьте, что вы получаете топ-10 данных в системе с огромным потоком информации; в систему добавляется все больше данных, следующие же 10 данных будут начальными 10-ю данными, которые вы уже получали. (мы не привязаны к элементам).
Допустим, skip(3).Take(3) вернет элементы [ 8, 7, 6 ], если нет уникального идентификатора сортировки. Но в следующий раз (сценарий 2), если при вызове пагинации информация уже была введена, мы снова получим те же данные, используя Skip(3).Take(3). ( [ 9, 8, 7] )
Пагинация на основе курсора
В отличие от пагинации на основе смещения, поскольку эта форма пагинации зависит от конкретных атрибутов сущности (в большинстве случаев от id), она всегда предоставляет уникальную информацию и по умолчанию перемещается вперед. В этом случае мы решаем вышеуказанную проблему, формируя команду типа "give 5 elements after id=7"
(предоставить 5 элементов после id=7).
GrahpQL использует пагинацию на основе курсора. Настроить пагинацию с помощью библиотеки hot chocolate очень просто. Вам просто нужно добавить декоратор [UsePaging]
в функции/резольверы запроса.
public class Query
{
[UsePaging(MaxPageSize = 2)]
public IQueryable<Customer> GetCustomers([Service] GraphQLAppDbContext graphqldbcontext)
=> graphqldbcontext.Customers.Include("Cards");
}
По умолчанию graphQL возвращает на страницу 10 элементов. Однако это можно изменить с помощью параметра MaxPagesize
. Если снова посмотреть на схему graphQL после добавления атрибута UsePaging, мы увидим в ней некоторые изменения.
Чтобы получить доступ к конфигурации пагинации, в интерфейс добавляется элемент pageInfo, а к элементам сущностей мы можем обращаться через nodes (узлы).
Чтобы получить следующий элемент, нам нужно использовать индекс курсора, определенный как endcursor, и указать, какое количество элементов мы хотим получить дальше.
Материал подготовлен в преддверии старта курса "C# Developer. Professional".