Run GraphQL queries using OdataToEntity
The article describes how to translate GraphQL queries into OData and execute them by writing
quite a bit of C # code.
How it works
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 performing the resulting expression tree (expression tree). Queries on various ORMs are performed through the abstract class OeDataAdapter and its implementation for:
- Entity Framework OeEf6DataAdapter
- Entity Framework Core OeEfCoreDataAdapter
- Linq2Db OeLinq2DbDataAdapter
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 .
Usage example
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 adaptervar dataAdapter = new StarWars.StarWarsDataAdapter(false, "test");
//build odata model
IEdmModel edmModel = dataAdapter.BuildEdmModelFromEfCoreModel();
//create graphql query parservar 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",
CASEWHEN"t"."Id"ISNULLTHENCAST(1ASBIT) ELSECAST(0ASBIT)
END,
"t"."Name"AS"Item10",
"t"."Id"AS"Item20",
CASEWHEN"EpisodeEnum"."Value"ISNULLTHENCAST(1ASBIT) ELSECAST(0ASBIT)
END,
"EpisodeEnum"."Name"AS"Item11",
"EpisodeEnum"."Value"AS"Item21"FROM"Hero"AS"h"LEFTJOIN"HeroToHero"AS"CharacterToCharacter"ON"h"."Id" = "CharacterToCharacter"."CharacterId"LEFTJOIN (
SELECT"Hero".*
FROM"Hero"AS"Hero"WHERE"Hero"."CharacterType"IN (1, 2)
) AS"t"ON"CharacterToCharacter"."FriendId" = "t"."Id"LEFTJOIN"HeroToEpisode"AS"CharacterToEpisode"ON"t"."Id" = "CharacterToEpisode"."CharacterId"LEFTJOIN"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.
Source code structure
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 .