0

For the API controller at https://github.com/CivicSpaceAu/CivicSpace/blob/main/src/Server/CivicSpace.Api/Controllers/GraphQLController.cs when using GraphiQL with URL https://localhost:7271/ui/graphiql I am getting an error

Cannot resolve 'MyApp.Api.AppQuery' from root provider because it requires scoped service 'MyApp.Data.Content.Repositories.Interfaces.INodeRepository'.

which happens here

    public AppSchema(IServiceProvider provider) : base(provider)
    {
        Query = provider.GetRequiredService<AppQuery>();
        Mutation = provider.GetRequiredService<AppMutation>();
    }

AppQuery needs INodeRepository, as defined in its constructor.

    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")));
    }

For some reason, DI can't find the the scoped INodeRepository, despite NodeRepository being set using

services.AddScoped<INodeRepository, NodeRepository>();

within ConfigureDataServices. The rest of Main is:

    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();
    }
Guru Stron
  • 102,774
  • 10
  • 95
  • 132
Jonas Arcangel
  • 2,085
  • 11
  • 55
  • 85

1 Answers1

1

Here AppSchema will be created only once instance and shared across all requests.

builder.Services.AddSingleton<ISchema, AppSchema>(services => 
    new AppSchema(new SelfActivatingServiceProvider(services)));

AppQuery requires the scoped service INodeRepository, which cannot be resolved correctly in a singleton lifetime scope. you should register the AppSchema as a scoped service instead of a singleton.By doing this, the AppSchema will be created once per scope (request).

builder.Services.AddScoped<ISchema, AppSchema>(services => 
    new AppSchema(new SelfActivatingServiceProvider(services)));

AddScoped will correctly resolve the scoped service INodeRepository when needed by AppQuery.The error should be resolved, and you should be able to use GraphiQL without issues.

Option 2: Another way to get the instance of scoped dependency is to inject service provider (IServiceProvider) into the middleware constructor, create scope in Invoke method and then get the required service from the scope:

using (var scope = _serviceProvider.CreateScope()) {
    var _emailRepository = scope.ServiceProvider.GetRequiredService<IEmailRepository>();

    //do your stuff....
}

ref: link link1, link2, link3

MD. RAKIB HASAN
  • 3,670
  • 4
  • 22
  • 35