Динамическая генерация 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-операции можно использовать следующие подходы:
- Использовать метод IGraphQLService.RequestAsync
IGraphQLService service;
...
var queryStr = RequestBuilder.From("system_file")
.ToGraphQL();
string resultStr = await service.RequestAsync(queryStr, GraphqlParams.Default);
- Использовать метод расширения 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);