You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Hot Chocolate中实现Customer实体多源数据单次REST请求?

解决方案

在Hot Chocolate v12.15.0 + .NET 6环境下,你可以通过DataLoader实现只调用一次REST接口同时获取contact和status字段,既保证两个字段作为顶层字段存在,又避免重复请求。以下是具体实现步骤:

1. 定义REST返回的DTO

先创建对应REST接口响应的实体类,用于接收接口返回的数据:

public class CustomerContactStatusDto
{
    public string Id { get; set; }
    public string Contact { get; set; }
    public string Status { get; set; }
}

2. 实现批量数据加载器(DataLoader)

DataLoader是Hot Chocolate提供的批量处理+缓存工具,能自动合并相同请求、避免重复调用数据源:

public class CustomerContactStatusDataLoader : BatchDataLoader<string, CustomerContactStatusDto>
{
    private readonly HttpClient _httpClient;

    public CustomerContactStatusDataLoader(
        IBatchScheduler batchScheduler,
        IHttpClientFactory httpClientFactory)
        : base(batchScheduler)
    {
        _httpClient = httpClientFactory.CreateClient("CustomerApi");
    }

    protected override async Task<IReadOnlyDictionary<string, CustomerContactStatusDto>> LoadBatchAsync(
        IReadOnlyList<string> customerIds,
        CancellationToken cancellationToken)
    {
        // 处理批量请求:如果REST接口支持批量ID参数,直接一次请求;否则并行请求(控制并发)
        var queryParams = string.Join("&", customerIds.Select(id => $"id={Uri.EscapeDataString(id)}"));
        var response = await _httpClient.GetFromJsonAsync<CustomerContactStatusDto[]>(
            $"/url/customer?{queryParams}", cancellationToken);

        // 将返回结果转为字典,方便按ID快速查找
        return response?.ToDictionary(dto => dto.Id) ?? new Dictionary<string, CustomerContactStatusDto>();
    }
}

3. 扩展Customer实体的GraphQL字段

通过[ExtendObjectType]特性,为EF中的Customer实体添加contact和status两个顶层字段,两个字段共享同一个DataLoader的缓存结果:

[ExtendObjectType(typeof(Customer))]
public class CustomerGraphQLExtensions
{
    public async Task<string> GetContact(
        [Parent] Customer customer,
        CustomerContactStatusDataLoader dataLoader,
        CancellationToken cancellationToken)
    {
        var contactStatus = await dataLoader.LoadAsync(customer.Id, cancellationToken);
        return contactStatus?.Contact;
    }

    public async Task<string> GetStatus(
        [Parent] Customer customer,
        CustomerContactStatusDataLoader dataLoader,
        CancellationToken cancellationToken)
    {
        var contactStatus = await dataLoader.LoadAsync(customer.Id, cancellationToken);
        return contactStatus?.Status;
    }
}

4. 注册服务

在Program.cs中注册HttpClient、DataLoader和GraphQL相关服务:

var builder = WebApplication.CreateBuilder(args);

// 注册REST接口的HttpClient
builder.Services.AddHttpClient("CustomerApi", client =>
{
    client.BaseAddress = new Uri("https://your-rest-api-base-url/");
});

// 注册GraphQL服务
builder.Services.AddGraphQLServer()
    .AddQueryType<Query>() // 你的Query类型
    .AddType<CustomerGraphQLExtensions>()
    .AddDataLoader<CustomerContactStatusDataLoader>();

var app = builder.Build();

app.MapGraphQL();
app.Run();

原理说明

  • 当GraphQL请求同时包含contact和status字段时,DataLoader会自动收集所有需要查询的Customer ID,批量调用一次REST接口。
  • 接口返回的数据会被缓存,contact和status字段的解析器会从缓存中读取同一个DTO对象,分别返回对应属性值,完全避免重复请求。
  • 即使请求只包含其中一个字段,DataLoader也只会调用一次接口,后续如果再请求另一个字段,会直接使用缓存数据。

内容的提问来源于stack exchange,提问作者mortware

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 02:40:40