如何在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
相关产品推荐
相关产品推荐

