You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Startup类添加含UserManager的Singleton服务时遇作用域解析错误

解决无法从根容器解析Scoped服务UserManager的问题

你遇到的InvalidOperationException: Cannot resolve scoped service 'Microsoft.AspNetCore.Identity.UserManager1[ibasis_api.Areas.Identity.Data.ibasis_apiUser]' from root provider`错误,核心原因是服务生命周期不匹配:

  • UserManager<TUser>是ASP.NET Core Identity默认注册的Scoped服务(每个请求创建一次实例)
  • 而你把依赖它的IJWTAuthenticationManager和ITokenRefresher注册成了Singleton服务(应用启动时创建,一直存活到应用关闭)

Singleton服务的生命周期远长于Scoped服务,.NET Core的依赖注入容器禁止从根容器(Singleton所属的容器)解析Scoped服务,避免出现Scoped服务被长期持有、导致状态污染或资源泄漏的问题。

最推荐的解决方案:调整服务生命周期为Scoped

将IJWTAuthenticationManager和ITokenRefresher的注册从AddSingleton改为AddScoped,让它们和UserManager的生命周期保持一致:

修改Startup类中对应的服务注册代码:

// RefreshTokenGenerator是无状态的,可以保持Singleton,也可以改成Scoped,不影响
services.AddSingleton<IRefreshTokenGenerator, RefreshTokenGenerator>();

// 把下面两个服务改成Scoped
services.AddScoped<IJWTAuthenticationManager>(x => new JWTAuthenticationManager(
    tokenKey, 
    x.GetService<IRefreshTokenGenerator>(), 
    x.GetService<UserManager<ibasis_apiUser>>()));

services.AddScoped<ITokenRefresher>(x => new TokenRefresher(
    key, 
    x.GetService<IJWTAuthenticationManager>(), 
    x.GetService<UserManager<ibasis_apiUser>>()));

这样修改后,每个请求会创建一个IJWTAuthenticationManager和ITokenRefresher的实例,它们可以正常获取当前请求的UserManager实例,符合ASP.NET Core处理用户请求的设计模式。

备选方案:若必须使用Singleton(不推荐)

如果因为特殊需求必须保持IJWTAuthenticationManager为Singleton,可以通过注入IServiceScopeFactory手动创建Scoped来获取UserManager:

步骤1:修改JWTAuthenticationManager的构造函数

public class JWTAuthenticationManager : IJWTAuthenticationManager
{
    private readonly string tokenKey;
    private readonly IRefreshTokenGenerator refreshTokenGenerator;
    private readonly IServiceScopeFactory _scopeFactory; // 注入Scope工厂

    public JWTAuthenticationManager(
        string tokenKey, 
        IRefreshTokenGenerator refreshTokenGenerator, 
        IServiceScopeFactory scopeFactory)
    {
        this.tokenKey = tokenKey;
        this.refreshTokenGenerator = refreshTokenGenerator;
        _scopeFactory = scopeFactory;
    }

步骤2:在需要使用UserManager的方法中创建Scope

public async Task<AuthenticationResponse> Authenticate(string username, string password)
{
    using (var scope = _scopeFactory.CreateScope()) // 创建Scoped容器
    {
        var userManager = scope.ServiceProvider.GetRequiredService<UserManager<ibasis_apiUser>>();
        var user = await userManager.FindByNameAsync(username);
        
        if(user != null && !await userManager.CheckPasswordAsync(user, password))
        {
            return null;
        }

        var token = GenerateTokenString(username, DateTime.UtcNow);
        var refreshToken = refreshTokenGenerator.GenerateToken();
        user.RefreshTokens = refreshToken;
        await userManager.UpdateAsync(user);

        return new AuthenticationResponse { JwtToken = token, RefreshToken = refreshToken };
    } // 自动释放Scope,回收Scoped服务
}

同样的,TokenRefresher也需要做类似的修改,注入IServiceScopeFactory来动态获取UserManager。

注意:这种方式需要手动管理Scope的创建和释放,容易出错,且不符合ASP.NET Core处理用户请求的常规模式,仅在特殊场景下使用。

额外检查点

  • 确认你的IdentityContext注册正确,你代码中同时调用了AddIdentityCore和AddIdentity,可以简化为只调用AddIdentity,因为AddIdentity已经包含了AddIdentityCore的功能
  • RefreshTokenGenerator如果是无状态的,保持Singleton是没问题的,不会影响依赖注入。

内容的提问来源于stack exchange,提问作者user964787

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 08:12:34