2) Пока кэширования не предусмотрено, надо подумать в эту сторону. Кстати, хорошая идея, чтобы добавить возможность запрос и при помощи строк для большей вариативности, хоть я и хотел от этого уйти, но лишним не будет, если появится
По производительности могу добавить, что ничего сверхъестественного здесь нет - для строк используется StringBuilder, для запросов в graphql-серверу - обычный HttpClient, а для десериализации ответа - System.Text.Json
Ну и самое главное, что если в схеме что-то изменится, при перегенерации QueryContext эти изменения сломают компиляцию, что очень сильно поможет в избежании проблем.
Да, я задумывался о том, чтобы не возиться со строками, чтобы при компиляции ловить ошибки, т.к. клиент все-таки типизированный. Но еще можно сделать инфраструктурный Nuget-пакет с выходными моделями, и в нужном сервисе подключать его. Обновится Nuget-пакет - если изменились модели, будет ошибка на этапе компиляции
Но для этого, конечно, нужно следить за актуальностью версии Nuget-пакета в проекте
Понял теперь о чем речь. Ну я думаю, что большинство функционала, который не является стандартным в том же Linq, и здесь можно оставить на откуп пользователя. Кому понадобится что-то необычное - добавит свой extension себе локально. В комментариях уже были примеры со SkipAndTake , в принципе, и это можно реализовать, если кому-то понадобится
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 будет вылезать.
Потому что далеко не все доступные методы-расширения Linq подходят для GraphQL-запроса. Мы, например, не можем выполнить какие-нибудь Average, Sum, Aggregate, AsNoTracking и т.д. GraphQL-сервер от ChilliCream без расширений на стороне сервера поддерживает только фильтрацию, сортировку, проекцию, пагинацию и FirstOrDefault, поэтому проще сделать свой интерфейс по типу IQueryable только для GraphQL, чтобы однозначно определить методы, которые точно будет поддерживаться. Иначе пришлось бы из большей половины Linq-методов кидать исключения, что они не поддерживаются
Постараюсь в ближайшее время написать про работу с Expression'ами в этом проекте. Ничего сильно сложного здесь нет, но есть неочевидные подводные камни, с которыми пришлось столкнуться
Согласен. Немного кликбейтный получился заголовок. Да, я не задавался целью реализовать свой собственный QueryProvider, хотел просто сделать похожий на Linq api, чтобы было удобно и привычно пользоваться таким GraphQL-клиентом. Под капотом здесь своя реализация некоторого IQueryable (IGraphQLQueryable), ничего общего с обычным Linq он не имеет
1) При помощи этой библиотеки - нет, но должна быть точно не одна библиотека для генерации классов из схемы.
Можно посмотреть в сторону StrawberryShake - https://chillicream.com/docs/strawberryshake/v13/tooling
2) Пока кэширования не предусмотрено, надо подумать в эту сторону. Кстати, хорошая идея, чтобы добавить возможность запрос и при помощи строк для большей вариативности, хоть я и хотел от этого уйти, но лишним не будет, если появится
По производительности могу добавить, что ничего сверхъестественного здесь нет - для строк используется StringBuilder, для запросов в graphql-серверу - обычный HttpClient, а для десериализации ответа - System.Text.Json
Да, я задумывался о том, чтобы не возиться со строками, чтобы при компиляции ловить ошибки, т.к. клиент все-таки типизированный. Но еще можно сделать инфраструктурный Nuget-пакет с выходными моделями, и в нужном сервисе подключать его. Обновится Nuget-пакет - если изменились модели, будет ошибка на этапе компиляции
Но для этого, конечно, нужно следить за актуальностью версии Nuget-пакета в проекте
Понял теперь о чем речь. Ну я думаю, что большинство функционала, который не является стандартным в том же Linq, и здесь можно оставить на откуп пользователя. Кому понадобится что-то необычное - добавит свой extension себе локально. В комментариях уже были примеры со
SkipAndTake
, в принципе, и это можно реализовать, если кому-то понадобитсяНу от ошибок и опечаток никто не застрахован. Идея с QueryContext мне нравится ?
Добавлю еще один вариант. Можно на стороне сервера возвращать заранее отфильтрованную/отсортированную IQueryable<>, например. Например:
Permission'ы - т.е. доступ на основе Permission'ов нужно делать на стороне GraphQL-сервера. Думаю, в сети можно найти примеры, погуглив что-то типа "PermissionBased Authorization HotChocolate".
Чтобы возвращать не все поля - тут посложнее будет, т.к. GraphQL-схема штука статичная и должна быть заранее известна при обращении к ней. Самый простой метод - это создать DTO и предоставить пользователю метод, возвращающий, например, список таких DTO. Тогда у пользователя не будет доступа до полей, которые ему не нужно видеть.
Можно и в рантайме вычищать ответ от GraphQL-сервера, ставля в null, например, какие-то поля, до которых у пользователя нет доступа, но, если честно, мне это не нравится. Нужно будет тогда еще и в фильтрацию, сортировку залазить, чтобы по недоступным полям выкидывать из запроса фильтрацию/сортировку. В общем, те еще костыли с велосипедами
Вынесите в класс с константами и проблем не будет.
Схема не генерируется. Не добавлял такой функциональности. В принципе, и так полно инструментов, которые генерируют классы из GraphQL-схемы.
Сделать типизированный класс-Endpoint - хорошая идея, мне нравится. Только я бы через конструктор тогда уж сохранял строку, а не через атрибут. В проекте есть возможность добавить атрибут [GraphQLEndpoint("users")] на сущность, но надо понимать, что одна и та же сущность может использоваться разных методах с разными endpoint'ами, поэтому лучше либо передавать строки, либо сделать такой типизированный класс-endpoint.
Не вижу смысла, т.к. это уже не похоже на Linq. Цель была - сделать удобный для всех инструмент взаимодействия с GraphQL-сервером. Чтобы все, кто умеет работать с Linq на примере того же EntityFramework, без проблем поняли, что и зачем тут происходит.
Первый раз такое вижу, но если кому-то сильно надо, то можно же написать свой собственный метод-расширения.
В 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 он не имеет