
L'article décrit comment traduire des requêtes GraphQL en OData et les exécuter en écrivant
pas mal de code C #.
Comment ça marche
L'idée principale du projet est la traduction des requêtes GraphQL en OData, la traduction des requêtes OData en un arbre d'expression, qui est ensuite traduit en une requête pour ORM. L'analyse d'une requête GraphQL et la sérialisation de ses résultats se font à l'aide de GraphQL pour .NET . L'analyse des requêtes OData est effectuée à l'aide des bibliothèques OData .NET . La traduction des requêtes (GraphQL -> OData -> arborescence d'expression) et leur exécution sont effectuées à l'aide d' OdataToEntity .
L'accès direct aux données s'effectue via l'ORM exécutant l'arborescence d'expression résultante. Les requêtes sur divers ORM sont exécutées via la classe abstraite OeDataAdapter et son implémentation pour:
- Entity Framework OeEf6DataAdapter
- Entity Framework Core OeEfCoreDataAdapter
- Linq2Db OeLinq2DbDataAdapter
L'utilisateur n'a besoin que d'un contexte d'accès aux données (EF / EF Core - DbContext, Linq2Db - DataConnection).
Pour plus d'informations sur l'exécution des requêtes OData, consultez mon article précédent, OdataToEntity, un moyen simple de créer des services OData .Net Core .
Exemple d'utilisation
Par exemple, nous utiliserons le schéma Star Wars , ORM EF Core, le fournisseur SQLite en mémoire.
Vous devez d'abord créer un contexte d'accès aux données StarWarsContext . Ensuite, l'adaptateur d'accès aux données StarWarsDataAdapter . Une fois que vous pouvez commencer à exécuter la demande:
String query = @" { human(id: ""1"") { name friends { name appearsIn { name } } } } ";
Requête GraphQL:
{ human(id: ""1"") { name friends { name appearsIn { name } } } }
Traduit en OData:
Human?$filter=Id eq '1'&$select=Name&$expand=Friends($select=Name;$expand=AppearsIn($select=Name))
Traduit en SQL:
SELECT "h"."Name" AS "Item1", "h"."Id" AS "Item2", CASE WHEN "t"."Id" IS NULL THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT) END, "t"."Name" AS "Item10", "t"."Id" AS "Item20", CASE WHEN "EpisodeEnum"."Value" IS NULL THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT) END, "EpisodeEnum"."Name" AS "Item11", "EpisodeEnum"."Value" AS "Item21" FROM "Hero" AS "h" LEFT JOIN "HeroToHero" AS "CharacterToCharacter" ON "h"."Id" = "CharacterToCharacter"."CharacterId" LEFT JOIN ( SELECT "Hero".* FROM "Hero" AS "Hero" WHERE "Hero"."CharacterType" IN (1, 2) ) AS "t" ON "CharacterToCharacter"."FriendId" = "t"."Id" LEFT JOIN "HeroToEpisode" AS "CharacterToEpisode" ON "t"."Id" = "CharacterToEpisode"."CharacterId" LEFT JOIN "Episodes" AS "EpisodeEnum" ON "CharacterToEpisode"."EpisodeId" = "EpisodeEnum"."Value" WHERE ("h"."CharacterType" = 1) AND ("h"."Id" = @__Item1_0)
Résultat JSON:
{ "data": { "human": [ { "name": "Luke", "friends": [ { "name": "R2-D2", "appearsIn": [ { "name": "NEWHOPE" }, { "name": "EMPIRE" }, { "name": "JEDI" } ] }, { "name": "C-3PO", "appearsIn": [ { "name": "NEWHOPE" }, { "name": "EMPIRE" }, { "name": "JEDI" } ] } ] } ] } }
Le SQL généré n'a pas de problème de requêtes N + 1, toutes les données sont obtenues en une seule requête.
Structure du code source
Le code source est divisé en deux parties: dans le dossier source - la bibliothèque elle-même et les assemblys d'accès pour diverses sources de données, dans le dossier de test - tests et exemples de code.
La bibliothèque elle-même se trouve dans le dossier source / OdataEntity.GraphQL .
Tests test / OdataToEntity.Test.GraphQL .
Le fichier de solution sln / OdataToEntity.Test.GraphQL.sln .