GraphQL: better match with EntityFramework

GraphQL is not only a query language for API, but also a runtime to meet your data query. GraphQL provides an easy to understand and complete description of the data in your API, so that the client can accurately obtain the data it needs without any redundancy. It also makes it easier for the API to evolve over time and can be used to build powerful developer tools.

-- from https://graphql.cn

For query, more often, the data is in the structured database. The API service queries the database through ORM, and the API provides external calls with different URLs; Imagine if we pass ADO Net to access the database, how can we fully adapt to the flexible query mode of graphql through one statement? This is a difficult point. You can only query all the data sets and let graphql filter the data you need in memory. In this way, a large number of data sets will soon occupy all the memory. It is not advisable. Of course, the way of writing sql directly like dapper is not enough.

At this time, the advantages of EF are revealed. In fact, EF itself is used by background programmers and encapsulates the function of converting Linq expressions to sql, so that background programmers don't have to care about sql statements; Here, if you can connect GraphQL and Linq, you can realize the GraphQL interface, and the background development becomes easier; Just in time, the sky makes a pair. GraphQL meets EF, making the two "seamless".

Michael Staib did the same and brought HotChocolate. The following is a case of GraphQL+EF(sql server).

Add Nuget package

  HotChocolate.AspNetCore

  HotChocolate.Data

  HotChocolate.Data.EntityFramework

using HotChocolate;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace GraphQLDemo01
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddPooledDbContextFactory<AdventureWorks2016Context>(
                (services, options) => options
                .UseSqlServer(Configuration.GetConnectionString("ConnectionString"))
                .UseLoggerFactory(services.GetRequiredService<ILoggerFactory>()))
                .AddGraphQLServer()
                .AddQueryType<Query>()
                .AddFiltering()
                .AddSorting()
                .AddProjections();
        }
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseRouting();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapGraphQL();
            });
        }
    }
}

The case database is AdventureWorks, the official demo database of sql server. Each query method becomes very simple. You only need to return the corresponding entity set, but the return value must be iqueryabl < >. It is this feature that makes GraphQL and EF fit so well. A paging is added to the feature. Considering the large amount of data, HotChocolate brings paging very considerate. The entity class and Context generated by AdventureWorks will not be displayed.

using System.Linq;
using HotChocolate;
using HotChocolate.Data;
using HotChocolate.Types;
namespace GraphQLDemo01
{
    public class Query
    {
        [UseDbContext(typeof(AdventureWorks2016Context))]
        [UseOffsetPaging]
        [UseProjection]
        [UseFiltering]
        [UseSorting]
        public IQueryable<Product> GetProducts([ScopedService] AdventureWorks2016Context context)
        {
            return context.Products;
        }

        [UseDbContext(typeof(AdventureWorks2016Context))]
        [UsePaging]
        [UseProjection]
        [UseFiltering]
        [UseSorting]
        public IQueryable<Person> GetPersons([ScopedService] AdventureWorks2016Context context)
        {
            return context.People;
        }
    }
}

Use the products whose color is red and sort by listPrice

{
   products(where: { color:{  eq:"Red"}   } order:[{listPrice:ASC}]) {
       items{
            productId
            name
            listPrice
       }
     }
 }

Using paging to query person

{
   persons( order: [{ businessEntityId: ASC }] after:"MTk="){
      pageInfo{
        hasNextPage
        hasPreviousPage
        startCursor
        endCursor
      }
      nodes{
         businessEntityId
         firstName
         middleName
         lastName
         emailAddresses{
            emailAddressId
            emailAddress1
            modifiedDate
         }
      }
      edges{
         cursor
         node{
            businessEntityId
         }
      }
     }     
 }

Use offsetpaging to query products

{
   products( order: [{ productId: ASC }] skip:40 take:20){
      pageInfo{
        hasNextPage
        hasPreviousPage    
      }
      items{
         productId
         name
      }
   }     
 }

If you track the sql statements of these queries, you will find that the generated sql will limit the query range, which can improve the memory utilization. Of course, the credit lies in EF, which is not what GraphQL needs to do. This is also ADO Net and dapper classes are matched with GraphQL.

With the same GrapQL, the following figure is the dapper query statement for the Product table.

 

The following figure shows the statement generated by EF. The statement generated by EF is more accurate.

 

 

You can pay more attention to WeChat official account if you want to know more quickly and conveniently.
 

 

 

Keywords: .NET

Added by Porkie on Tue, 01 Feb 2022 09:49:28 +0200