Search code examples
.netgraphqlentity-framework-core.net-5hotchocolate

GraphQL Hot Chocolate Constructor DI failed on 2nd request


I am building a GraphQL endpoint with a user authentication service. For the user authentication, I'm using Entity Framework Core.

In the mutation resolver, when I used the constructor DI [Ref], I am getting below error when accessing the resolver for 2nd time. "Cannot access a disposed object.\r\nObject name: 'UserManager`1'."

As per the Hot Chocolate instruction, constructor DIs are singleton types, and not quite sure why I am getting this error.

However, If I use [Service] key word to inject in the resolver method. I am not getting any errors [Ref].

public void ConfigureServices(IServiceCollection services)
        {
            services.AddGraphQLService();

            services.AddDbContext<DataContext>(opt =>
            {
                opt.UseSqlite(_configuration.GetConnectionString("AuthenticationConnection"));
            });

            services.AddIdentityCore<User>(opt =>
            {
                opt.Password.RequireNonAlphanumeric = false;
            })
                .AddRoles<IdentityRole>()
                .AddEntityFrameworkStores<DataContext>()
                .AddSignInManager<SignInManager<User>>();

        }

This is the extension method that configures the GrapghQL

public static IServiceCollection AddGraphQLService(this IServiceCollection services)
        {
            services.AddGraphQLServer()
                .AddQueryType(q => q.Name("Query"))
                .AddTypeExtension<AlbumQueryTypeExtension>()
                .AddTypeExtension<ArtistQueryTypeExtension>()
                .AddMutationType(m => m.Name("Mutation"))
                .AddTypeExtension<ArtistMutationTypeExtension>()
                .AddTypeExtension<AuthenticationMutationTypeExtension>()
                .AddSubscriptionType<Subscription>()
                .AddInMemorySubscriptions()
                .AddAuthorization()
                ;

            return services;
        }

In my resolver, the below method works,

public async Task<User> LoginAsync(LoginDto loginDto, [Service] UserManager<User> userManager)
        {
            var user = await userManager.FindByEmailAsync(loginDto.Email);
            
            return user ;
        }

But if I do constructor DI, I get an error.

public class AuthenticationMutateResolvers
    {
        private readonly UserManager<User> _userManager;
            
        public AuthenticationMutateResolvers(UserManager<User> userManager)
        {
            _userManager = userManager;
        }


        public async Task<User> LoginAsync(LoginDto loginDto)
        {
            var user = await _userManager.FindByEmailAsync(loginDto.Email);
            
            return user;
        }
}

Any idea why constructor DI only works for the first request.

Anything I am doing wrong?


Solution

  • So, If you do not declare anything Hot Chocolate will add the resolver classes as singleton. In your case with UserManager<User> I think it is a scoped service. So every resolver class that uses constructor injection in your case needs to be registered in the DI as well as a scoped service.

    Alternatively you can use resolver level DI.

    public async Task<User> LoginAsync(LoginDto loginDto, [Service] UserManager<User> userManager)
    {
        var user = await userManager.FindByEmailAsync(loginDto.Email);
                
        return user;
    }
    

    I personally tend to use resolver level DI since its more clear what is needed during execution. But this is up to you.