使用Hot Chocolate和.NET 6構建GraphQL應用(3) —— 實現Query基礎功能

系列導航

使用Hot Chocolate和.NET 6構建GraphQL應用文章索引

需求

在本文中,我們通過一個簡單的例子來看一下如何實現一個最簡單的GraphQL的介面。

實現

引入Hot Chocolate依賴包

由於我打算將GraphQL的相關邏輯放到Applicaiton層,並在Application和Api項目中使用,所以在該項目中引入以下依賴包

# Hot Chocolate在.NET Web應用中使用的主要Nuget包
HotChocolate.AspNetCore
# Hot Chocolate集成EntityFramework Core底層ORM框架的Nuget包
HotChocolate.Data.EntityFramework
# Hot Chocolate的一些屬性定義擴展
HotChocolate.Data
# 一個可視化的GraphQL Schema中間件
GraphQL.Server.Ui.Voyager

添加Resolver

Api項目中添加文件夾GraphQL用於存放GraphQL介面相關的定義,並新建Query.cs文件,我們在這裡定義一個介面:

  • Query.cs
namespace PostGraphi.Api.GraphQL;

public class Query
{
    // [Service]是Hot Chocolate提供的用於獲取依賴注入對象的屬性
    public IQueryable<Post> GetPosts([Service] IRepository<Post> repository) => repository.GetAsQueryable();
}

需要在GlobalUsings.cs中添加以下命名空間:

  • GlobalUsings.cs
global using HotChocolate;
global using PostGraphi.Domain.Post.Entities;

添加依賴注入

  1. 添加GraphQL的服務:
builder.Services
    .AddGraphQLServer()
    .AddQueryType<Query>();
  1. 添加GraphQL終結點配置和Voyager中間件
app.UseHttpsRedirection();

// 添加GraphQL終結點配置
app.UseRouting().UseEndpoints(endpoints =>
{
    endpoints.MapGraphQL();
});

// 添加Voyager中間件並配置URL
app.UseGraphQLVoyager(new VoyagerOptions { GraphQLEndPoint = "/graphql" }, "/graphql-voyager");

app.UseAuthentication();
app.UseAuthorization();

驗證

運行Api項目,首先我們訪問地址://localhost:7194/graphql-voyager 查看Voyager的主頁:

img

可以看到我們目前只有一個schema,名稱叫做posts,該介面所關聯的實體以及實體關聯的其他實體組成的圖非常清楚地顯示出來了。這張圖上我們能看到DomainEvents也顯示出來了,但這不是我們想要的,這個問題我們稍後來解決。

再去查看Hot Chocolate官方提供的隨程式一起啟動的Banana Cake Pop主頁://localhost:7194/graphql/

img

在這個頁面上我們可以查看具體的Schema Definition和全部的GraphQL Server所維護的類型定義。也可以從Operations標籤頁直接發起GraphQL請求。

query {
  posts {
    id,
    title,
    author,
    comments {
      content
    }
    tags {
      name
    }
  }
}

如果使用API Client發起GraphQL請求,注意請求是POST //localhost:7194/graphql,Body內容是GraphQL Query,像這樣:

img

在執行請求之後,我們先來看看控制台的EFCore日誌輸出:

SELECT "p"."Id", "p"."Abstraction", "p"."Author", "p"."Content", "p"."Created", "p"."CreatedBy", "p"."LastModified", "p"."LastModifiedBy", "p"."Link", "p"."PublishedAt", "p"."Title"
FROM "Posts" AS "p"

敏銳的小夥伴可能會發現,這個sql里並沒有看到和Comments和Tags相關的Join之類的操作,下面的通過Banana Cake Pop請求響應也同樣能說明這個問題:

img

在這個返回結果里,我們至少能看到兩類問題:一是Comments和Tags這種一對多和多對多的關聯表數據並沒有同時返回;二是返回似乎沒有排序。這兩個問題我們會在後面的文章里逐個去解決。

不管怎麼樣,我們的第一個GraphQL介面已經調用成功了。下面來解決上面說到的DomainEvents的問題。

改進

因為我們默認使用了Annotation First方式去構建介面,並且是直接使用的實體對象,並沒有做任何配置,因為我並不想將業務相關的介面屬性直接配置到我的Domain Model上,這個時候就需要使用Code First方式去為Post類定義對應的GraphQL類。

Api/GraphQL中新建文件夾Types並創建PostType.cs,讓它繼承自Hot Chocolate提供的ObjectType<T>類型,並重寫Configure方法:

  • PostType.cs
namespace PostGraphi.Api.GraphQL.Types;

public class PostType : ObjectType<Post>
{
    protected override void Configure(IObjectTypeDescriptor<Post> descriptor)
    {
        descriptor.Description("Represents Post Entity Type.");
        // 構建schema時忽略這個欄位
        descriptor.Field(d => d.DomainEvents).Ignore();
    }
}

需要在GlobalUsings.cs中引入命名空間:

  • GlobalUsings.cs
global using HotChocolate.Types;

最後在依賴注入的地方添加Type即可:

builder.Services
    .AddGraphQLServer()
    .AddQueryType<Query>()
    .AddType<PostType>();

重新運行程式,我們在Voyager主頁上查看,DomainEvents已經消失了:

img

總結

在本文中我們實現了一個最簡單的GraphQL介面,並且使用了Hot Chocolate提供的兩種方式Annotation FirstCode First實現了功能。其實關於Code First方式還有一個比較重要的概念叫做Resolver我們在文章演示中沒有涉及,這個概念可以理解為通過Code First方式去定義GraphQL對象時,我們可以使用ResolveWith來指定使用什麼Resolver去獲取數據。更多的配置方法可以參考官方文檔:Resolvers

本文我們還留下了兩個問題,下一篇文章將會實現關聯實體對象的獲取方式。

參考文章

  1. Resolvers