概述
我们终于可以添加recommendedPlaylists
字段 到一个 Recipe
!
在本课中,我们将
- 学习多个 子图 如何贡献 字段 到一个 实体
定义实体
The Recipe
类型已标记为 实体 在 recipes
子图 中。我们可以通过查看 Studio 中的 Schema 页面并选择 Objects 来确认这一点。
在 Recipe
类型旁边是 E
标签,表示它是一个 实体 类型。点击该类型,我们可以进一步看到该实体的关键 字段: id
.
这太好了!The recipes
子图 已经为我们打开了向该 实体 贡献 字段 的途径。让我们继续这样做,回到代码编辑器中的项目。
让我们在 Types
文件夹中创建一个名为 Recipe
的新类。
namespace Odyssey.MusicMatcher;public class Recipe{}
记住,要定义一个 实体,我们需要两样东西:一个主键 ([Key]
) 和一个引用 解析器 ([ReferenceResolver]
).
定义主键
首先,让我们在文件顶部导入
HotChocolate.ApolloFederation
包。Types/Recipe.csusing HotChocolate.ApolloFederation;在
Recipe
类中,我们将添加Id
作为类的属性,使用 自动实现属性的简写语法。注意,这里我们使用大写Id
来遵循 C# 惯例,但在幕后,Hot Chocolate 在生成 GraphQL 模式 时会将其转换为小写。此属性返回string
类型。Types/Recipe.cspublic string Id { get; }我们还将使用
[ID]
属性来注释此属性。尽管Id
返回string
类型,但我们希望将其映射到 GraphQL 标量 类型ID
,而不是String
.Types/Recipe.cs[ID]public string Id { get; }最后,我们将使用
Key
属性标记该属性,它将其指定为主键。Types/Recipe.cs[ID][Key]public string Id { get; }
定义引用解析器函数
除了主键之外,我们还需要一个引用 解析器:一个返回特定实体实例的函数。
让我们定义一个新的函数,它将是
public static
。我们将将其命名为GetRecipeById
。引用 解析器 的名称并不重要,但我们应该对它正在做什么进行描述。此函数将返回Recipe
类型 (即 实体 类型!)。Types/Recipe.cspublic static Recipe GetRecipeById(){}要将其标记为 实体 的引用 解析器,我们将使用
[ReferenceResolver]
属性。Types/Recipe.cs[ReferenceResolver]public static Recipe GetRecipeById()对于此函数的参数,我们可以访问 实体 的关键 字段 (s)。在本例中,即
id
属性。Types/Recipe.cspublic static Recipe GetRecipeById(string id)在函数主体中,我们将使用
id
返回Recipe
类的实例来实例化它。Types/Recipe.csreturn new Recipe(id);最后,我们将为
Recipe
类编写构造函数,它接受string id
并将其分配给Id
属性。Types/Recipe.cspublic Recipe(string id){Id = id;}
贡献实体
太棒了,我们已经填好了 stub,它定义了新的 子图 中实体的必要条件:定义实体所需的最小值。现在,让我们添加新的 字段 来实现我们的梦想 查询:食谱的推荐播放列表。
在
Recipe
类的主体内部,添加一个名为RecommendedPlaylists
的新 解析器 函数。现在,我们将使用硬编码数据。我们将在下一课中将其替换为对我们 数据源 的调用。The
RecommendedPlaylists
方法将返回一个Playlist
对象列表。Types/Recipe.cspublic List<Playlist> RecommendedPlaylists(){return new List<Playlist>{new Playlist("1", "GraphQL Groovin'"),new Playlist("2", "Graph Explorer Jams"),new Playlist("3", "Interpretive GraphQL Dance")};}让我们也为这个 字段 添加一个描述。
Types/Recipe.cs[GraphQLDescription("A list of recommended playlists for this particular recipe. Returns 1 to 3 playlists.")]
注册实体
最后一步:我们需要将这种类型注册到我们的GraphQL 服务器。打开Program.cs
,找到我们初始化GraphQL 服务器的那一行。为了将所有类似的方法调用分组,我们将在注册其他类型(如Query
和Mutation
)之后添加下一行:AddType<Recipe>()
。
builder.Services.AddGraphQLServer().AddApolloFederation().AddQueryType<Query>().AddMutationType<Mutation>().AddType<Recipe>().RegisterService<SpotifyService>();
好了,让我们保存更改并重新启动服务器。
该rover dev
进程将拾取更改并负责将这些更改与recipes
子图组合起来。如果一切顺利,我们就可以查询这个新模式了!
在资源管理器中查询
跳回到https://127.0.0.1:4000,我们的本地rover dev
路由器正在运行。
让我们开始构建一个新的操作,一个我们梦想的查询的简化版本。我们将询问特定食谱的名称及其推荐播放列表的名称。
query GetRecipeWithPlaylists {randomRecipe {namerecommendedPlaylists {idname}}}
哇哦——我们正在获取数据——在烹饪食谱时可以唱的播放列表,不错!
让我们看一眼查询计划。单击Response旁边下拉箭头,选择Query Plan。
图表视图显示了对recipes
子图的初始获取,然后是对soundtracks
子图的另一个获取,最后合并(或扁平化)recipe
类型。单击Show plan as text。
QueryPlan {Sequence {Fetch(service: "recipes") {{recipe(id: "rec3j49yFpY2uRNM1") {__typenameidname}}},Flatten(path: "recipe") {Fetch(service: "soundtracks") {{... on Recipe {__typenameid}} =>{... on Recipe {recommendedPlaylists {idname}}}},},},}
这里我们获得更多详细信息。首先,路由器将操作发送到recipes
子图,以获取食谱的__typename
、id
和name
。我们没有在原始操作中包含__typename
或id
,但路由器需要它们来进行查询计划中的下一步,以构建实体表示形式Recipe
!
该路由器将该实体表示形式发送到soundtracks
子图,后者使用这些信息来识别Recipe的该实例,并为其贡献更多字段:特别是recommendedPlaylists
字段。
太棒了!我们的recipes
和soundtracks
服务现在正在协同处理同一个Recipe
实体。每个子图都贡献自己的字段,而路由器为我们打包响应。
练习
ReferenceResolver
。关键要点
- 在 Hot Chocolate 中,我们使用
[Key]
属性来定义字段的键,并使用[ReferenceResolver]
属性来定义引用解析器函数。 - 在 Hot Chocolate 中,实体需要注册到GraphQL 服务器。
接下来
我们对音乐的推荐仍然很弱。事实上,它们一点也不像推荐——它们是硬编码的播放列表对象!我们还没有真正使用任何特定食谱的数据来确定我们向潜在厨师用户推荐的播放列表。在下一课中,我们将深入探讨与联合相关的指令,并加强我们的音轨建议。
分享您对本课的疑问和意见
本课程目前处于
您需要一个 GitHub 帐户才能在下面发布。没有帐户吗? 请在 Odyssey 论坛上发布。