Pull to refresh
4
0
Роман Давыденко @RDavydenko

.NET разработчик

Send message

1) При помощи этой библиотеки - нет, но должна быть точно не одна библиотека для генерации классов из схемы.
Можно посмотреть в сторону StrawberryShake - https://chillicream.com/docs/strawberryshake/v13/tooling

2) Пока кэширования не предусмотрено, надо подумать в эту сторону. Кстати, хорошая идея, чтобы добавить возможность запрос и при помощи строк для большей вариативности, хоть я и хотел от этого уйти, но лишним не будет, если появится

По производительности могу добавить, что ничего сверхъестественного здесь нет - для строк используется StringBuilder, для запросов в graphql-серверу - обычный HttpClient, а для десериализации ответа - System.Text.Json

Ну и самое главное, что если в схеме что-то изменится, при перегенерации QueryContext эти изменения сломают компиляцию, что очень сильно поможет в избежании проблем.

Да, я задумывался о том, чтобы не возиться со строками, чтобы при компиляции ловить ошибки, т.к. клиент все-таки типизированный. Но еще можно сделать инфраструктурный Nuget-пакет с выходными моделями, и в нужном сервисе подключать его. Обновится Nuget-пакет - если изменились модели, будет ошибка на этапе компиляции

Но для этого, конечно, нужно следить за актуальностью версии Nuget-пакета в проекте

Понял теперь о чем речь. Ну я думаю, что большинство функционала, который не является стандартным в том же Linq, и здесь можно оставить на откуп пользователя. Кому понадобится что-то необычное - добавит свой extension себе локально. В комментариях уже были примеры со SkipAndTake , в принципе, и это можно реализовать, если кому-то понадобится

Ну от ошибок и опечаток никто не застрахован. Идея с QueryContext мне нравится ?

Добавлю еще один вариант. Можно на стороне сервера возвращать заранее отфильтрованную/отсортированную IQueryable<>, например. Например:

public IQueryable<Entity> GetMyEntities(
  [Service] IUnitOfWork unitOfWork, 
  [Service] IUserAccessor userAccessor)
{
    var userId = userAccessor.GetCurrentUserId();
    return unitOfWork.Entities.Where(x => x.CreateUserId == userId);
}

Permission'ы - т.е. доступ на основе Permission'ов нужно делать на стороне GraphQL-сервера. Думаю, в сети можно найти примеры, погуглив что-то типа "PermissionBased Authorization HotChocolate".

Чтобы возвращать не все поля - тут посложнее будет, т.к. GraphQL-схема штука статичная и должна быть заранее известна при обращении к ней. Самый простой метод - это создать DTO и предоставить пользователю метод, возвращающий, например, список таких DTO. Тогда у пользователя не будет доступа до полей, которые ему не нужно видеть.

Можно и в рантайме вычищать ответ от GraphQL-сервера, ставля в null, например, какие-то поля, до которых у пользователя нет доступа, но, если честно, мне это не нравится. Нужно будет тогда еще и в фильтрацию, сортировку залазить, чтобы по недоступным полям выкидывать из запроса фильтрацию/сортировку. В общем, те еще костыли с велосипедами

По идее было бы лучше избежать текстовых значений. Банальная опечатка проблемы создаст.

Вынесите в класс с константами и проблем не будет.

Если схема генерируется на базе SDL

Схема не генерируется. Не добавлял такой функциональности. В принципе, и так полно инструментов, которые генерируют классы из GraphQL-схемы.

Сделать типизированный класс-Endpoint - хорошая идея, мне нравится. Только я бы через конструктор тогда уж сохранял строку, а не через атрибут. В проекте есть возможность добавить атрибут [GraphQLEndpoint("users")] на сущность, но надо понимать, что одна и та же сущность может использоваться разных методах с разными endpoint'ами, поэтому лучше либо передавать строки, либо сделать такой типизированный класс-endpoint.

Определить интерфейсы и сделать что-то типа

Include<IRoles>() .ThenInclude<IUsers>()

или даже

Include<IRoles, IUsers>()

Не вижу смысла, т.к. это уже не похоже на Linq. Цель была - сделать удобный для всех инструмент взаимодействия с GraphQL-сервером. Чтобы все, кто умеет работать с Linq на примере того же EntityFramework, без проблем поняли, что и зачем тут происходит.

Часто вместе идут, можно сахара добавить SkipAndTake(5, 10)

Первый раз такое вижу, но если кому-то сильно надо, то можно же написать свой собственный метод-расширения.

Можно ещё добавить собственный анализатор, чтобы в compile time ловить не поддерживаемые expressions. Иначе определённый тип ошибок только в runtime будет вылезать.

В compile time - это, конечно, круто

Потому что далеко не все доступные методы-расширения Linq подходят для GraphQL-запроса. Мы, например, не можем выполнить какие-нибудь Average, Sum, Aggregate, AsNoTracking и т.д. GraphQL-сервер от ChilliCream без расширений на стороне сервера поддерживает только фильтрацию, сортировку, проекцию, пагинацию и FirstOrDefault, поэтому проще сделать свой интерфейс по типу IQueryable только для GraphQL, чтобы однозначно определить методы, которые точно будет поддерживаться. Иначе пришлось бы из большей половины Linq-методов кидать исключения, что они не поддерживаются

Спасибо за комментарий. Если интересно покопаться в исходниках, вот исходный код Visitor'а, который строит Where-выражение. После рефакторинга получился совсем худенький - https://github.com/RDavydenko/SmartGraphQLClient/blob/master/src/SmartGraphQLClient.Core/Visitors/WhereExpressionVisitor/WhereExpressionVisitor.cs

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

Согласен. Немного кликбейтный получился заголовок. Да, я не задавался целью реализовать свой собственный QueryProvider, хотел просто сделать похожий на Linq api, чтобы было удобно и привычно пользоваться таким GraphQL-клиентом. Под капотом здесь своя реализация некоторого IQueryable (IGraphQLQueryable), ничего общего с обычным Linq он не имеет

Information

Rating
Does not participate
Registered
Activity

Specialization

Backend Developer
C#
.NET
ASP.NET Web API
React
TypeScript