能否注册同CLR类型的多份HotChocolate ObjectTypes用于不同查询?
问题与解决方案
问题描述
我们直接从EF Core Model获取数据,目前已注册了一个带配置的ObjectType<Doc>。想注册第二个同CLR类型但配置不同(忽略部分属性、自定义属性命名等)的对象类型,分别用于不同Query方法,但尝试后收到错误:The name 'X' was already registered by another type。
示例场景:
ObjectType<Doc>:已创建的映射至EF Core模型的类型,供getDoc()查询使用ObjectType<DocSimplified>:想要创建的仅返回部分属性且带有不同命名的第二个类型,供getDocSimplified()查询使用
解决方案
方法1:给第二个ObjectType指定唯一名称
报错核心原因是Hot Chocolate默认用CLR类型名作为GraphQL对象类型的名称,同一个CLR类型注册两次必然导致名称冲突。解决办法很简单:在注册第二个ObjectType<Doc>时,显式指定一个唯一的类型名称,同时配置对应的属性规则。
示例代码:
// 原有的完整类型配置 public class DocType : ObjectType<Doc> { protected override void Configure(IObjectTypeDescriptor<Doc> descriptor) { // 原有配置:映射EF Core模型的所有属性 descriptor.Field(d => d.Id).Type<IdType>(); descriptor.Field(d => d.FullContent).Type<StringType>(); descriptor.Field(d => d.CreatedAt).Type<DateTimeType>(); // 其他完整属性的配置 } } // 简化类型的配置,重点是指定唯一名称 public class DocSimplifiedType : ObjectType<Doc> { protected override void Configure(IObjectTypeDescriptor<Doc> descriptor) { // 关键操作:给这个类型指定唯一名称,避免和原类型冲突 descriptor.Name("DocSimplified"); // 配置仅需要的属性,支持自定义命名或忽略不需要的属性 descriptor.Field(d => d.Id).Type<IdType>(); descriptor.Field(d => d.Title).Name("docTitle"); // 自定义属性名称 descriptor.Field(d => d.CreatedAt).Type<DateTimeType>(); // 忽略不需要的属性 descriptor.Ignore(d => d.FullContent); descriptor.Ignore(d => d.OtherUnneededProperty); } }
之后在注册查询和Schema时,把两个类型都加进去:
public class Query { // 返回完整的Doc类型 public async Task<Doc> GetDoc([Service] AppDbContext context, int id) { return await context.Documents.FindAsync(id); } // 返回简化版的DocSimplified类型 public async Task<Doc> GetDocSimplified([Service] AppDbContext context, int id) { return await context.Documents.FindAsync(id); } } // 注册Schema时添加两个ObjectType services.AddGraphQLServer() .AddQueryType<Query>() .AddType<DocType>() .AddType<DocSimplifiedType>();
这样GraphQL层面会识别出两个不同的类型(Doc和DocSimplified),各自按配置返回对应的属性,不会再出现名称冲突。
方法2:用DTO类(可选方案)
如果允许新增类,也可以定义一个DocSimplified DTO,只包含需要的属性,再给这个DTO注册对应的ObjectType,完全规避同CLR类型的冲突问题。查询时从EF Core的Doc对象映射到DTO返回就行。
示例代码:
// 定义简化版DTO public class DocSimplified { public int Id { get; set; } public string DocTitle { get; set; } public DateTime CreatedAt { get; set; } } // 对应的ObjectType配置 public class DocSimplifiedType : ObjectType<DocSimplified> { protected override void Configure(IObjectTypeDescriptor<DocSimplified> descriptor) { descriptor.Field(d => d.Id).Type<IdType>(); descriptor.Field(d => d.DocTitle).Type<StringType>(); descriptor.Field(d => d.CreatedAt).Type<DateTimeType>(); } } // 查询方法中做对象映射 public async Task<DocSimplified> GetDocSimplified([Service] AppDbContext context, int id) { var doc = await context.Documents.FindAsync(id); return new DocSimplified { Id = doc.Id, DocTitle = doc.Title, CreatedAt = doc.CreatedAt }; }
这种方式逻辑更清晰,适合属性差异大、后续维护需求高的场景。
内容的提问来源于stack exchange,提问作者gneric
相关产品推荐
相关产品推荐

