启用GraphQL的API出现作用域仓储无法解析的DI错误
我使用GraphiQL访问GraphQL API(访问地址:https://localhost:7271/ui/graphiql)时,遇到如下错误:
无法从根提供程序解析'MyApp.Api.AppQuery',因为它需要作用域服务'MyApp.Data.Content.Repositories.Interfaces.INodeRepository'。
错误发生在AppSchema的构造函数中:
public AppSchema(IServiceProvider provider) : base(provider) { Query = provider.GetRequiredService<AppQuery>(); Mutation = provider.GetRequiredService<AppMutation>(); }
AppQuery的构造函数依赖INodeRepository:
public AppQuery(INodeRepository nodeRepository) { _nodeRepository = nodeRepository; Name = nameof(AppQuery); Field<ListGraphType<NodeType>>("node") .Argument<StringGraphType>("id", "node id") .ResolveAsync(async context => await _nodeRepository.GetByIdAsync(context.GetArgument<string>("id"))); }
我已在ConfigureDataServices中通过services.AddScoped<INodeRepository, NodeRepository>()注册了作用域的INodeRepository,但依赖注入仍无法找到它。以下是Main方法的完整代码:
public async static Task Main(string[] args) { var builder = WebApplication.CreateBuilder(args); var configurationBuilder = new ConfigurationBuilder() .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile("appsettings.local.json", optional: true, reloadOnChange: true) .AddEnvironmentVariables(); var configuration = configurationBuilder.Build(); builder.Services.AddSingleton<IConfigurationRoot>(configuration); // NodeRepository added by this: builder.Services.ConfigureDataServices(configuration); builder.Services.AddSingleton<ISchema, AppSchema>(services => new AppSchema(new SelfActivatingServiceProvider(services))); builder.Services.AddGraphQL(b => b //.AddHttpMiddleware<ISchema>() //.AddUserContextBuilder(httpContext => new GraphQLUserContext { User = httpContext.User }) .AddSystemTextJson() .AddSchema<AppSchema>() .AddGraphTypes(typeof(AppSchema).Assembly)); builder.Services.AddControllers(); var app = builder.Build(); app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.UseGraphQL<AppSchema>("/graphql"); app.UseGraphQLGraphiQL(); app.Run(); }
核心原因
你将AppSchema注册为单例服务,但在它的构造函数中直接解析了依赖作用域服务的AppQuery。单例服务的生命周期覆盖整个应用程序,而作用域服务是每个请求创建一次——根服务提供者(单例所在的容器)无法解析作用域服务,因为此时不存在请求作用域。
修复步骤
1. 调整AppSchema的注册生命周期
将AppSchema从单例改为作用域注册,让它随每个请求创建:
// 替换原来的AddSingleton注册代码 builder.Services.AddScoped<ISchema, AppSchema>();
2. 修改AppSchema构造函数,直接注入依赖
不要手动从IServiceProvider获取AppQuery和AppMutation,让DI容器自动注入:
public AppSchema(AppQuery query, AppMutation mutation) : base() { Query = query; Mutation = mutation; }
3. 注册AppQuery和AppMutation为作用域服务
如果未注册这两个类,需添加以下代码:
builder.Services.AddScoped<AppQuery>(); builder.Services.AddScoped<AppMutation>();
调整后的Main方法关键片段
// ... 其他配置代码 builder.Services.ConfigureDataServices(configuration); // 新增Query和Mutation的作用域注册 builder.Services.AddScoped<AppQuery>(); builder.Services.AddScoped<AppMutation>(); // 修改Schema的注册为作用域 builder.Services.AddScoped<ISchema, AppSchema>(); builder.Services.AddGraphQL(b => b .AddSystemTextJson() .AddSchema<AppSchema>() .AddGraphTypes(typeof(AppSchema).Assembly)); // ... 其他配置代码
补充说明
作用域服务必须在请求作用域内解析,单例服务在应用启动时就已初始化,此时没有请求上下文,自然无法解析作用域依赖。通过让AppSchema也成为作用域服务,它会在每个请求时被创建,此时可以正常解析同为作用域的AppQuery、AppMutation,进而解析它们的依赖INodeRepository。
内容的提问来源于stack exchange,提问作者Jonas Arcangel

