HotChocolate如何根据不同查询在DI中注入对应接口实现类
HotChocolate按查询域注入不同接口实现方案
完全可以实现该需求,核心是利用HotChocolate的解析上下文配合DI键控服务/工厂模式完成动态实例选择,以下是具体实现步骤:
1. 基础服务注册
首先将两个IPrinter实现以键控服务的形式注入DI容器(.NET 8+原生支持键控服务,低版本可自行实现工厂逻辑代替):
// 注册不同域对应的IPrinter实现 builder.Services.AddKeyedSingleton<IPrinter, PhysicalPrinter>("Customer"); builder.Services.AddKeyedSingleton<IPrinter, Digitalprinter>("Administration");
2. 实现服务动态选择逻辑
方案A:自定义工厂+显式调用
适合需要灵活控制实例选择的场景,先实现工厂类封装选择逻辑:
public interface IPrinterFactory { IPrinter GetPrinter(IResolverContext context); } public class PrinterFactory : IPrinterFactory { private readonly IServiceProvider _serviceProvider; public PrinterFactory(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public IPrinter GetPrinter(IResolverContext context) { // 可根据实际业务规则替换判断逻辑 // 规则示例1:按查询根类型判断(拆分CustomerQuery和AdministrationQuery两个根类的场景) if (context.ObjectType.Name == "CustomerQuery") { return _serviceProvider.GetRequiredKeyedService<IPrinter>("Customer"); } if (context.ObjectType.Name == "AdministrationQuery") { return _serviceProvider.GetRequiredKeyedService<IPrinter>("Administration"); } // 规则示例2:按查询字段的自定义属性判断(同一根查询下不同字段分域的场景) var domainAttr = context.Selection.Field.GetCustomAttribute<DomainAttribute>(); if (domainAttr != null) { return _serviceProvider.GetRequiredKeyedService<IPrinter>(domainAttr.DomainName); } throw new NotSupportedException("当前查询域无匹配的Printer实现"); } }
如果使用自定义属性做域标记,需要先定义属性类:
[AttributeUsage(AttributeTargets.Method)] public class DomainAttribute : Attribute { public string DomainName { get; } public DomainAttribute(string domainName) => DomainName = domainName; }
注册工厂到DI:
builder.Services.AddSingleton<IPrinterFactory, PrinterFactory>();
在查询解析器中使用:
// 拆分根查询场景 public class CustomerQuery { public string GetPrintInfo([Service] IPrinterFactory factory, IResolverContext context) { var printer = factory.GetPrinter(context); return printer.Print(); } } // 同一根查询+自定义属性场景 public class Query { [Domain("Customer")] public string CustomerPrint([Service] IPrinterFactory factory, IResolverContext context) => factory.GetPrinter(context).Print(); [Domain("Administration")] public string AdminPrint([Service] IPrinterFactory factory, IResolverContext context) => factory.GetPrinter(context).Print(); }
方案B:自动注入(无需修改现有解析器入参)
如果希望直接注入IPrinter就自动匹配对应实现,可以在GraphQL服务配置中自定义解析逻辑:
builder.Services.AddGraphQLServer() // 注册IPrinter的动态解析规则 .AddService<IPrinter>(ServiceKind.Resolver, sp => { var context = sp.GetRequiredService<IResolverContext>(); var factory = sp.GetRequiredService<IPrinterFactory>(); return factory.GetPrinter(context); }) // 其他配置保持不变 .AddQueryType<Query>();
配置完成后即可直接在解析器中注入IPrinter使用,无需手动调用工厂:
[Domain("Customer")] public string CustomerPrint([Service] IPrinter printer) { return printer.Print(); }
内容的提问来源于stack exchange,提问作者Makus
相关产品推荐
相关产品推荐

