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:


    1. Entity Framework OeEf6DataAdapter
    2. Entity Framework Core OeEfCoreDataAdapter
    3. 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 .


    Also popular now: