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

如何在HotChocolate中对GraphQL联合类型进行专属属性过滤?

问题:联合类型中各子类型非共享属性的过滤实现

我在.NET 6上使用HotChocolate(12.14.0)和Entity Framework Core(6.0.9)搭建了一个可获取文件与文件夹的GraphQL端点,文件和文件夹通过UnionType定义为联合类型,但生成的过滤输入仅支持共享属性(Id、Name),无法针对File的Size这类专属属性过滤,想知道能不能实现对联合类型中各类型非共享属性的过滤。

现有代码如下:

public class Query
{
    [UseOffsetPaging(IncludeTotalCount = true)]
    [UseProjection]
    [UseFiltering]
    [UseSorting]
    public IQueryable<IFileOrFolder> GetFiles([Service] IFilesReadModel filesReadModel)
        => filesReadModel.GetFiles();
}

[UnionType("FileOrFolder")]
public interface IFileOrFolder
{
    int Id { get; set; }

    string Name { get; set; }
}

[Table("files")]
public abstract class FileOrFolder : IFileOrFolder
{
    public int Id { get; set; }

    public string Name { get; set; } = string.Empty;
}

public class File : FileOrFolder
{
    public int Size { get; set; }
}

public class Folder : FileOrFolder
{
    public ICollection<FileOrFolder> Files { get; set; } = new List<FileOrFolder>();
}

生成的Schema片段:

type Query {
  ...
  filesAndFolders(
    skip: Int
    take: Int
    where: IFileOrFolderFilterInput
    order: [IFileOrFolderFilterInput!]
  ): FileOrFolderCollectionSegment
  ...
}

input IFileOrFolderFilterInput {
  and: [IFileOrFolderFilterInput!]
  or: [IFileOrFolderFilterInput!]
  id: ComparableInt32OperationFilterInput
  name: StringOperationFilterInput
}

解决方案

可以实现,通过自定义过滤输入类型结合类型条件判断,就能支持子类型的专属属性过滤,具体步骤如下:

1. 定义自定义过滤输入类

创建包含共享属性、子类型专属过滤和逻辑组合符的输入结构:

// 联合类型的总过滤输入
public class FileOrFolderFilterInput
{
    // 共享属性过滤项
    public Int32OperationFilterInput? Id { get; set; }
    public StringOperationFilterInput? Name { get; set; }

    // 子类型专属过滤,通过类型标识区分
    public FileFilterInput? IsFile { get; set; }
    public FolderFilterInput? IsFolder { get; set; }

    // 逻辑组合符
    public List<FileOrFolderFilterInput>? And { get; set; }
    public List<FileOrFolderFilterInput>? Or { get; set; }
}

// File专属过滤输入
public class FileFilterInput
{
    public Int32OperationFilterInput? Size { get; set; }
    // 可添加其他File专属属性,比如后缀名等
}

// Folder专属过滤输入(按需添加)
public class FolderFilterInput
{
    // 比如可添加子项数量过滤等属性
}

2. 配置GraphQL过滤逻辑

在Program.cs(或Startup)的GraphQL服务配置中,替换默认过滤并绑定自定义逻辑:

builder.Services.AddGraphQLServer()
    .AddQueryType<Query>()
    .AddType<File>()
    .AddType<Folder>()
    .AddUnionType<IFileOrFolder>("FileOrFolder")
    .AddFiltering()
    // 注册自定义过滤输入类型
    .AddFilterInputType<FileOrFolderFilterInput>()
    // 绑定过滤逻辑到EF查询
    .BindFilteringContext<IFileOrFolder>((ctx, descriptor) =>
    {
        // 绑定共享属性的过滤解析
        descriptor.AddField(x => x.Id);
        descriptor.AddField(x => x.Name);

        // 处理File的专属属性过滤
        descriptor.AddField("isFile", (query, filter) =>
        {
            if (filter.IsFile?.Size != null)
            {
                // 转换为File类型后应用Size过滤,再转回到联合类型
                query = query.OfType<File>().Where(f => 
                    filter.IsFile.Size switch
                    {
                        { Equals: int val } => f.Size == val,
                        { GreaterThan: int val } => f.Size > val,
                        { LessThan: int val } => f.Size < val,
                        _ => true
                    }
                ).Cast<IFileOrFolder>();
            }
            return query;
        });

        // 绑定逻辑组合符的解析
        descriptor.AddAnd();
        descriptor.AddOr();
    });

3. 修改查询方法的过滤特性

在Query类的GetFiles方法上,指定使用自定义的过滤输入类型:

[UseOffsetPaging(IncludeTotalCount = true)]
[UseProjection]
[UseFiltering(typeof(FileOrFolderFilterInput))] // 指定自定义过滤类型
[UseSorting]
public IQueryable<IFileOrFolder> GetFiles([Service] IFilesReadModel filesReadModel)
    => filesReadModel.GetFiles();

4. 使用示例

此时生成的Schema会包含子类型过滤字段,你可以这样写查询:

query {
  filesAndFolders(
    where: {
      or: [
        { isFile: { size: { greaterThan: 1024 } } }, // 过滤大小超过1024的文件
        { name: { contains: "docs" } } // 过滤名称包含docs的文件/文件夹
      ]
    }
  ) {
    nodes {
      ... on File {
        id
        name
        size
      }
      ... on Folder {
        id
        name
      }
    }
  }
}

注意事项

  • 你的代码使用了EF Core的TPH(每个层次结构一个表)继承策略,数据库中会自动生成鉴别器列来区分File和Folder,OfType<File>()会自动利用这个列做类型筛选。
  • 以上代码适配HotChocolate 12.14.0版本,新版本配置方式可能略有差异。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 02:25:28