This article describes how to translate GraphQL queries to OData and write them.
quite a bit of C # code.
The main idea of the project is the translation of GraphQL queries into OData, the translation of OData queries into an expression tree, which is then translated into a query for ORM. GraphQL query parsing and its results are serialized using GraphQL for .NET . An OData request is parsed using the OData .NET Libraries . Query translation (GraphQL -> OData -> expression tree) and their execution is performed using OdataToEntity .
Direct access to the data is carried out through the ORM that executes the resulting expression tree (expression tree). Queries on various ORMs are performed through the abstract class OeDataAdapter and its implementation for:
The user is only required to have a data access context (EF / EF Core - DbContext, Linq2Db - DataConnection).
You can read more about the execution of OData requests in my previous article OdataToEntity an easy way to create .Net Core OData services .
For example, the Star Wars , ORM EF Core, SQLite in memory provider will be used.
First you need to create a data access context StarWarsContext . Then the data access adapter StarWarsDataAdapter . After you can start to run the query:
String query = @" { human(id: ""1"") { name friends { name appearsIn { name } } } } "; //create data adapter var dataAdapter = new StarWars.StarWarsDataAdapter(false, "test"); //build odata model IEdmModel edmModel = dataAdapter.BuildEdmModelFromEfCoreModel(); //create graphql query parser var parser = new OeGraphqlParser(edmModel); //get graphql result ExecutionResult result = await parser.Execute(query); //serialize json String json = new DocumentWriter(true).Write(result); Console.WriteLine(json);
GraphQL query:
{ human(id: ""1"") { name friends { name appearsIn { name } } } }
Broadcast to OData:
Human?$filter=Id eq '1'&$select=Name&$expand=Friends($select=Name;$expand=AppearsIn($select=Name))
Broadcast to 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)
JSON result:
{ "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" } ] } ] } ] } }
The generated SQL does not have the problem of N + 1 queries, all data is obtained in a single query.
The source code is divided into two parts: in the source folder, the library itself and assemblies of access to various data sources, in the test folder, tests and code samples.
The library itself is located in the source / OdataEntity.GraphQL folder .
Tests test / OdataToEntity.Test.GraphQL .
Solution file sln / OdataToEntity.Test.GraphQL.sln .
Source: https://habr.com/ru/post/429088/
All Articles