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?
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.