Перейти к основному содержимому

Динамическая генерация GraphQL-запросов в сервисе доступа к хранилищу данных

Для разработчиков сервисов бизнес-логики на C# Платформа предоставляет возможность работать не с объектами типа data context, которые должны собираться заново каждый раз при изменении модели данных, а с помощью динамически формируемых конструкций обращения к объектам по имени класса, передаваемом в запросе.

Для работы с динамическими конструкциями пользователю требуется доступ к реализации интерфейса IGraphQLService. Интрефейс IGraphQLService может быть получен из DI-контейнера после регистрации с использованием AddGraphqlService.

Функционал для динамической генерации GraphQL запросов находтся в пакете ASE.MD.Platform.Infrastructure.Store.GraphQL. Также потребуется пакет ASE.MD.Platform.Infrastructure.Store.ClientCore для низкоуровнего клиента GraphQL IGraphQLService.

Конечной точкой для динамической генерации GraphQL-запросов является статический класс RequestBuilder.

Запросы

Для создания запроса (query operation) используется метод From:

var queryOperation = RequestBuilder.From("system_file").

Параметром метода является имя резолвера, имеющее вид:

<имя проекта>_<имя класса> преобразованное в нижний регистр.

Для преобразования операции в строковое представление используется метод ToGraphQL:

string gqlQuery = queryOperation.ToGraphQL();

Для выполнения GraphQL-операции можно использовать следующие подходы:

  1. Использовать метод IGraphQLService.RequestAsync
IGraphQLService service;
...
var queryStr = RequestBuilder.From("system_file")
.ToGraphQL();
string resultStr = await service.RequestAsync(queryStr, GraphqlParams.Default);
  1. Использовать метод расширения GraphQLServiceExt.Execute
IGraphQLService service;
...
var query = RequestBuilder.From("system_file");
JObject result = await service.Execute(query);

Для выбора полей результата требуется воспользоваться методом Select

var query = RequestBuilder.From("system_file")
.Select("Id", "name", "deleted", "bucket.name");

Чтобы получить поле вложенной сущности, к нему следует обращаться используя символ «.»: "bucket.name".

Условия отбора

Для фильтрации выдачи используется выражение Where.

var query = RequestBuilder.From("system_file")
.Select("Id", "name", "deleted", "bucket.name")
.Where("deleted", ExpressionOperand.Equals, false);

Если в запросе требуется несколько вызовов Where, то их условия комбинируются через логическое «И»

var query = RequestBuilder.From("system_file")
.Select("Id", "name", "deleted", "bucket.name")
.Where("deleted", ExpressionOperand.Equals, false)
.Where("name", ExpressionOperand.StartWith, "a");

В качестве значения для сравнения в Условиях поддерживаются следующие типы данных:

  • числовые типы: int, long, float, double и др.;
  • string;
  • DateTime, DateTimeOffset;
  • bool;
  • Guid;
  • комбинации вышеперечисленных типов для операций In и NIn.

Если в фильтре необходимо условие по полю вложенной сущности, то к нему можно обратиться через «.»:

var query = RequestBuilder.From("system_file")
.Select("Id", "name", "deleted", "bucket.name")
.Where("bucket.name", ExpressionOperand.StartWith, "a");

Более сложные условия отбора конструируются с помощью метода RequestBuilder.Condition и методов раcширения OrElse и AndAlso:

string queryStr = RequestBuilder.From("system_file")
.Select("Id", "name", "bucket.Id", "bucket.name")
.Where(RequestBuilder.Condition("deleted", ExpressionOperand.NEquals, false)
.OrElse("name", ExpressionOperand.StartWith, "a")
.AndAlso("bucket.name", ExpressionOperand.Equals, "test")
)
.ToGraphQL();
подсказка

Порядок вызова мотодов OrElse и AndAlso имеет значение. Требуется обязательная проверка результата применения условий.

Пагинация и количество элементов

При обращении к сущностям с большим количеством записей используйте ограничение количества возвращаемых записей с помощью метода SkipTake:

string queryStr = RequestBuilder.From("system_file")
.Select("Id", "name", "bucket.Id", "bucket.name")
.Where("deleted", ExpressionOperand.Equals, false)
.SkipTake(10, 20)
.ToGraphQL();

Для получения информации о наличии данных перед и после текущей страницы используется метод AddPageInfo. В результат добавится объект pageInfo с полями hasNextPage и hasPreviousPage:

string queryStr = RequestBuilder.From("system_file")
.Select("Id", "name", "bucket.Id", "bucket.name")
.Where("deleted", ExpressionOperand.Equals, false)
.SkipTake(10, 20)
.AddPageInfo()
.ToGraphQL();

Для получения общего количества записей totalCount используется метод AddTotalCount:

string queryStr = RequestBuilder.From("system_file")
.Select("Id", "name", "bucket.Id", "bucket.name")
.Where("deleted", ExpressionOperand.Equals, false)
.SkipTake(10, 20)
.AddPageInfo()
.AddTotalCount()
.ToGraphQL();
предупреждение

Получение общего количества записей totalCount увеличивает время выполнения запроса, так как требует дополнительного обращения к базе данных. На больших объёмах данных это может быть весьма существенно. Не включайте в запрос totalCount если в нем нет реальной необходимости.

Сортировка

Для сортировки результата используется метод OrderBy:

string result = RequestBuilder.From("system_file")
.Select("Id", "name", "bucket.Id", "bucket.name")
.OrderBy("bucket.name", OrderDirection.Asc)
.OrderBy("name", OrderDirection.Asc)
.SkipTake(10, 20)
.ToGraphQL();

Если метод OrderBy применяется несколько раз, сортировка применяется в порядке в котором применен этот метод. В примере выше сортировка будет произведена сначала по полю bucket.name, затем по полю name.

Мутации

Мутации создаются так же как и запросы. Условия для Обновления и Удаления применяются такие же как и в запросах.

Вставка

var insert = RequestBuilder.Insert("system_file")
.Value("name", "aaa.txt")
.Value("bucket_id", "89976FEB-C184-43B0-9769-940F820B1D3F")
;
var result = await graphQLService.Execute(insert);

Обновление

var update=  RequestBuilder.Update("system_file")
.Where("Id", ExpressionOperand.Equals, "4600BD13-5A68-48C9-B573-612266AFAA2B")
.Value("name", "aaa.txt")
.Value("bucket_id", "89976FEB-C184-43B0-9769-940F820B1D3F")
;
var result = await graphQLService.Execute(update);

Удаление

var delete = RequestBuilder.Delete("system_file")
.Where("deleted", ExpressionOperand.Equals, false);
var result = await graphQLService.Execute(update);