如何使用DataLoader与Hot Chocolate GraphQL进行连接

问题描述

我看到可以使用Data Loader进行根查询,但是也可以使用Data Loader进行嵌套连接吗?在下面的示例中,我想为rooms属性使用数据加载器。在底部的示例请求中,将进行三个数据库查询一个由数据加载器来获取两个建筑物,一个由数据加载器来获取建筑物1的房间,另一个由数据加载器来获取建筑物2的房间。相反,我正在尝试为这些房间使用数据加载器,因此仅两个数据库查询制成。

// Building DB table
ID | Name
1  | Main Campus
2  | Satellite Campus
// Rooms DB table
ID | BuildingId | Name
1  | 1          | Lab
2  | 1          | Dorm
3  | 2          | Theatre
4  | 2          | Gym
// Schema
type Building {
  id: Int!
  name: String!
  rooms(after: String before: String first: PaginationAmount last: PaginationAmount): RoomsConnection
}

type Room {
  id: Int!
  name: String!
  building: Building!
}
// Hot Chocolate
public class BuildingType: ObjectType<Building> {
  protected override void Configure(IObjectTypeDescriptor<Building> descriptor)
  {
    // ... omitted other fields for brevity

    // Instead of using a resolver,can a data loader be used instead?
    descriptor.Field(b => b.rooms).UsePaging<RoomType>().Resolver(ctx => {
      var building = ctx.Parent<Building>();
      var roomsRepository = ctx.Service<IRoomsRepository>();
      return roomsRepository.GetRoomsByBuildingId(building.Id);
    });
  }
}
// Example request
query {
  a: building(id: 1){
    id,name,rooms {
      nodes {
        id,name
      }
    }
  },b: building(id: 2){
    id,name
      }
    }
  }
}

解决方法

这不仅是可能的,而且我想说这是DataLoader避免在对象树上进行“ N + 1”请求的基本用例。 为了您的目的,您可以使用GroupDataLoader。组数据加载器是批处理数据加载器,即它收集在一个graphql请求往返中对相似实体的请求,并将它们作为单个请求发送到数据源。之后,它会缓存结果-即在从对象树的任何位置请求时从缓存中返回实体。组数据加载器获取键列表作为输入参数(IReadOnlyList ),并返回缓存的实体列表作为查找(ILookup )。查找是一种数据类型,每个键包含多个值(而不是字典,每个键包含一个值)。

根据您的具体情况,必须按建筑物ID对房间进行分组-这是关键。因此,您可以通过以下方式使用组数据加载器来缓存您的连接(请注意,有必要更改RoomsRepository的接口以支持对其的批处理请求,即,它必须接受一个建筑物ID,而不是单个建筑物ID。其中的一批,我还假设“会议室”包含对其建筑物ID的反向引用):

   public class BuildingType : ObjectType<Building>
{
    protected override void Configure(IObjectTypeDescriptor<Building> descriptor)
    {
        descriptor.Field(b => h.rooms).UsePaging<RoomType>
.Resolver(async (ctx,t) => await ctx.GroupDataLoader<int,Room>("roomsByBuildingGroup",async keys => ctx.Service<IRoomsRepository>().GetRoomsByBuildingIds(keys).ToLookup(r => r.BuildingId))
                .LoadAsync(ctx.Parent<Building>().Id,t));

}

“ roomsByBuildingGroup”是加载程序的名称。为了共享缓存并将来自不同对象树位置的所有请求合并为一个请求,您必须在通过建筑物ID加载房间的所有其他位置使用相同的加载器(即,使用相同的加载器名称)。