知海

C# / .NET:graphql-net(将 GraphQL 转为 IQueryable)

graphql-github-io-zh-Hans生态项目与库

C# / .NET:graphql-net(将 GraphQL 转为 IQueryable)

概述

graphql-net 是一个面向 C# / .NET 的开源库,核心功能是将 GraphQL 查询直接转换为 IQueryable,从而让 GraphQL 查询能够与 Entity Framework 等 LINQ 提供程序无缝协作。通过这种转换,你可以在不牺牲性能的前提下,让客户端灵活地指定需要的数据字段,而服务端则使用数据库原生查询执行,避免加载不必要的数据。

核心特性

  • GraphQL → IQueryable 转换:自动将 GraphQL 选择集映射为 LINQ 表达式,生成可组合的 IQueryable
  • 与 Entity Framework 集成:直接作用于 DbSet 或自定义 IQueryable,查询在数据库端执行。
  • 类型安全:利用 C# 泛型和表达式树,在编译期尽可能发现字段映射错误。
  • 轻量、无侵入:可以只用于查询解析层,无需替换现有业务逻辑。
  • 支持自定义解析:允许你为复杂字段提供自定义 Resolve 委托。

工作原理

graphql-net 接收一个 GraphQL 查询请求,将其解析为抽象语法树(AST),然后遍历查询的选择集,根据目标类型(例如 IQueryable<Customer>)构建对应的 Expression<Func<T>>,最终生成一个新的 IQueryable。这样,返回的数据结构由客户端指定的字段决定,同时仍保持 LINQ 的延迟执行特性。

快速开始

以下示例演示如何将 GraphQL 查询应用于 IQueryable<Order>

csharp 复制代码
using GraphQL;
using GraphQL.Types;
using GraphQLNet;
using System.Linq;

// 定义数据模型
public class Order
{
    public int Id { get; set; }
    public string ProductName { get; set; }
    public decimal Total { get; set; }
    public Customer Customer { get; set; }
}

public class Customer
{
    public string Name { get; set; }
    public string Email { get; set; }
}

定义 GraphQL 类型:

csharp 复制代码
public class OrderType : ObjectGraphType<Order>
{
    public OrderType()
    {
        Field(x => x.Id);
        Field(x => x.ProductName);
        Field(x => x.Total);
        Field<CustomerType>("customer", resolve: ctx => ctx.Source.Customer);
    }
}

public class CustomerType : ObjectGraphType<Customer>
{
    public CustomerType()
    {
        Field(x => x.Name);
        Field(x => x.Email);
    }
}

使用 IQueryable 执行 GraphQL 查询:

csharp 复制代码
public async Task<string> ExecuteQueryAsync(IQueryable<Order> orders, string graphQlQuery)
{
    var schema = new Schema { Query = new OrderQuery() };
    var result = await schema.ExecuteAsync(_ =>
    {
        _.Query = graphQlQuery;
        _.Root = orders; // 传入 IQueryable
    });

    return Newtonsoft.Json.JsonConvert.SerializeObject(result);
}

其中 OrderQuery 定义根查询字段:

csharp 复制代码
public class OrderQuery : ObjectGraphType
{
    public OrderQuery()
    {
        Field<ListGraphType<OrderType>>(
            "allOrders",
            resolve: context =>
            {
                // context.Source 实际上是传入的 IQueryable
                var query = context.Source as IQueryable<Order>;
                return query; // graphql-net 将自动转换为 IQueryable 并处理选择集
            });
    }
}

与实体框架结合

假设使用 EF Core,你可以直接将 DbContextDbSet<T> 作为根:

csharp 复制代码
using var db = new AppDbContext();
var query = db.Orders.AsQueryable();
// 执行 GraphQL 查询,数据库只返回所选字段

graphql-net 生成的表达式会被 EF 翻译为 SQL,避免将整个实体加载到内存。

高级配置

  • 字段重命名:可以通过 Field(x => x.Property).Name("graphqlName") 来自定义 GraphQL 字段名。
  • 忽略字段:使用 Field<ListGraphType<OrderType>>("orders", resolve: ...) 时,未被选择的字段不会体现在 IQueryable 的投影中。
  • 中间件/过滤器:可结合 WithVariablesAuthorizeGraphQL.NET 扩展实现权限控制。

适用场景

  • 需要为现有 LINQ 数据源快速暴露 GraphQL API。
  • 希望避免手写大量 DTO 和映射代码。
  • 需要确保查询在数据库层执行,避免 N+1 问题。

注意事项

  • 仅支持同步/异步 IQueryable,不支持 IEnumerable 的完整树转换(需自行物化)。
  • 复杂父子关系可能需要额外调整表达式,建议先测试生成的 SQL。
  • 项目目前维护频率不高,建议评估后使用,或参考其实现自行扩展。

项目地址

帮助我们改进文档

发现翻译问题或内容错误?请告诉我们。