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

如何为多个GraphQL Schema实现依赖注入?Azure函数场景

解决Azure函数中多个GraphQL Schema冲突的问题

这个问题我之前碰到过,根源很明确:你在DI容器里多次注册ISchema接口时,容器会覆盖之前的注册记录,最终只有最后添加的SchemaTwo生效,这就是为什么执行queryOne会报错提示在QueryTwoType上找不到对应字段的原因。下面给你几个可行的解决方案,你可以根据业务场景选择:

方案一:创建独立的GraphQL端点(推荐)

这种方式最清晰,给每个Schema分配单独的路由,客户端请求不同的URL就会使用对应的Schema,完全避免冲突。

步骤1:注册具体的Schema类型

先别直接注册ISchema,而是注册每个Schema的具体实现:

// 注册SchemaOne的具体实例
builder.Services.AddTransient<SchemaOne>(sp => 
    new SchemaOne(new FuncDependencyResolver(type => (IGraphType)sp.GetRequiredService(type))));

// 注册SchemaTwo的具体实例
builder.Services.AddTransient<SchemaTwo>(sp => 
    new SchemaTwo(new FuncDependencyResolver(type => (IGraphType)sp.GetRequiredService(type))));

步骤2:创建两个独立的Azure函数触发器

分别对应两个Schema的请求处理:

// 处理SchemaOne的请求,路由为 /api/graphql/schemaone
[FunctionName("GraphQL_SchemaOne")]
public async Task<IActionResult> HandleSchemaOneRequest(
    [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "graphql/schemaone")] HttpRequest req,
    SchemaOne schema,
    ILogger log)
{
    // 解析GraphQL请求
    var requestBody = await new StreamReader(req.Body).ReadToEndAsync();
    var graphQLRequest = JsonSerializer.Deserialize<GraphQLRequest>(requestBody);

    // 执行查询
    var executionResult = await new DocumentExecuter().ExecuteAsync(options =>
    {
        options.Schema = schema;
        options.Query = graphQLRequest.Query;
        options.Variables = graphQLRequest.Variables;
        // 按需添加上下文、错误处理等配置
    });

    return new OkObjectResult(executionResult);
}

// 处理SchemaTwo的请求,路由为 /api/graphql/schematwo
[FunctionName("GraphQL_SchemaTwo")]
public async Task<IActionResult> HandleSchemaTwoRequest(
    [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "graphql/schematwo")] HttpRequest req,
    SchemaTwo schema,
    ILogger log)
{
    // 重复类似的请求处理逻辑,使用SchemaTwo执行查询
    var requestBody = await new StreamReader(req.Body).ReadToEndAsync();
    var graphQLRequest = JsonSerializer.Deserialize<GraphQLRequest>(requestBody);

    var executionResult = await new DocumentExecuter().ExecuteAsync(options =>
    {
        options.Schema = schema;
        options.Query = graphQLRequest.Query;
        options.Variables = graphQLRequest.Variables;
    });

    return new OkObjectResult(executionResult);
}

之后客户端只要请求对应的路由:

  • 调用queryOne时请求/api/graphql/schemaone
  • 调用queryTwo时请求/api/graphql/schematwo

方案二:使用命名服务(Keyed Services)动态选择Schema

如果想在同一个端点处理两个Schema,可以用.NET 6+支持的键控服务(Keyed Services)来区分不同的Schema,然后根据请求中的查询内容选择对应的Schema。

步骤1:注册键控的Schema服务

// 用"schemaOne"作为键注册SchemaOne
builder.Services.AddKeyedTransient<ISchema, SchemaOne>("schemaOne", sp => 
    new SchemaOne(new FuncDependencyResolver(type => (IGraphType)sp.GetRequiredService(type))));

// 用"schemaTwo"作为键注册SchemaTwo
builder.Services.AddKeyedTransient<ISchema, SchemaTwo>("schemaTwo", sp => 
    new SchemaTwo(new FuncDependencyResolver(type => (IGraphType)sp.GetRequiredService(type))));

步骤2:在函数中根据查询选择Schema

解析请求中的查询字符串,判断要使用哪个Schema:

[FunctionName("GraphQL_Combined")]
public async Task<IActionResult> HandleCombinedRequest(
    [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "graphql")] HttpRequest req,
    IServiceProvider serviceProvider,
    ILogger log)
{
    var requestBody = await new StreamReader(req.Body).ReadToEndAsync();
    var graphQLRequest = JsonSerializer.Deserialize<GraphQLRequest>(requestBody);

    // 根据查询内容判断使用哪个Schema
    ISchema targetSchema;
    if (graphQLRequest.Query.Contains("queryOne"))
    {
        targetSchema = serviceProvider.GetRequiredKeyedService<ISchema>("schemaOne");
    }
    else if (graphQLRequest.Query.Contains("queryTwo"))
    {
        targetSchema = serviceProvider.GetRequiredKeyedService<ISchema>("schemaTwo");
    }
    else
    {
        return new BadRequestObjectResult("无法识别的查询字段");
    }

    // 执行查询
    var executionResult = await new DocumentExecuter().ExecuteAsync(options =>
    {
        options.Schema = targetSchema;
        options.Query = graphQLRequest.Query;
        options.Variables = graphQLRequest.Variables;
    });

    return new OkObjectResult(executionResult);
}

注意:这种方式依赖查询字符串的内容判断,如果查询比较复杂(比如使用片段、变量),可能需要更严谨的解析逻辑,比如用GraphQL的语法解析器来提取根字段。

方案三:合并两个Schema到一个根Schema

如果业务允许,可以把两个Schema的查询类型合并成一个统一的根查询,这样所有字段都在同一个Schema中,自然不会有冲突。

步骤1:创建合并的根查询类型

// 假设SchemaOne的查询类型是SchemaOneQuery,SchemaTwo的是SchemaTwoQuery
public class CombinedRootQuery : ObjectGraphType
{
    public CombinedRootQuery(SchemaOneQuery schemaOneQuery, SchemaTwoQuery schemaTwoQuery)
    {
        // 将SchemaOne的查询作为子字段暴露
        Field<ObjectGraphType<SchemaOneQuery>>("queryOne", resolve: ctx => schemaOneQuery);
        // 将SchemaTwo的查询作为子字段暴露
        Field<ObjectGraphType<SchemaTwoQuery>>("queryTwo", resolve: ctx => schemaTwoQuery);
    }
}

步骤2:注册合并后的Schema

builder.Services.AddTransient<SchemaOneQuery>();
builder.Services.AddTransient<SchemaTwoQuery>();
builder.Services.AddTransient<CombinedRootQuery>();

// 注册合并后的Schema
builder.Services.AddTransient<ISchema>(sp => 
    new Schema(new FuncDependencyResolver(type => sp.GetRequiredService(type)))
    {
        Query = sp.GetRequiredService<CombinedRootQuery>()
    });

之后客户端就可以在同一个端点执行查询:

{
  queryOne {
    name
  }
}

和

{
  queryTwo {
    # SchemaTwo的字段
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:50:03