ASP.NET Core Identity中FindByIdAsync报DbContext并发异常
问题结论
这个报错和接口操作耗时没有任何关系,本质是同一个DbContext实例被两个并发的数据库操作同时访问,单步调试时所有操作串行执行,自然不会触发报错。
根因分析
你调整IAuthService生命周期没有解决问题,是因为没有定位到生命周期错配的核心点:
- 你当前使用
services.AddDbContext<Db>()注册的DbContext,默认生命周期是Scoped(请求级),正常逻辑下每个HTTP请求会生成独立的DbContext实例,不会跨请求共享。 - ASP.NET Core Identity默认注册的
UserManager<ApplicationUser>、RoleManager<ApplicationRole>也是Scoped生命周期,正常使用不会持有跨请求的DbContext。 - 触发这个错误的典型场景:
- 存在生命周期向上依赖问题:如果有注册为Singleton(单例)的服务(比如自定义过滤器、全局单例业务服务、后台服务)注入了
IAuthService、UserManager或者Db实例,哪怕你把IAuthService注册为Transient/Scoped,它持有的UserManager、DbContext也会随上层单例服务一直存活,多个并发请求访问时就会同时调用同一个DbContext实例,触发报错。 - 代码中存在未被await的异步调用:比如在调用
_userManager.FindByIdAsync前后,存在没有加await关键字的其他UserManager/DbContext异步方法调用,或者用了Task.Run开后台线程复用当前请求的DbContext实例,导致同一个请求内两个数据库操作并行执行。
- 存在生命周期向上依赖问题:如果有注册为Singleton(单例)的服务(比如自定义过滤器、全局单例业务服务、后台服务)注入了
修复步骤
按优先级依次排查修复:
- 全局检查所有服务注册代码,所有依赖DbContext、UserManager、RoleManager的服务,禁止注册为Singleton;如果确实需要在单例服务(比如后台任务、全局组件)中使用用户相关逻辑,不要直接注入上述Scoped服务,通过
IServiceScopeFactory手动创建作用域解析实例,示例:
// 单例服务中正确使用UserManager的写法 private readonly IServiceScopeFactory _scopeFactory; public SingletonBackgroundService(IServiceScopeFactory scopeFactory) { _scopeFactory = scopeFactory; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { // 手动创建独立作用域 using var scope = _scopeFactory.CreateScope(); var userManager = scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>(); // 所有数据库操作都在作用域内执行,执行完自动释放相关实例 var user = await userManager.FindByIdAsync(targetUserId); }
- 检查所有异步代码逻辑,所有返回
Task/Task<T>的异步调用必须添加await关键字,不要在异步流程中混用.Result、.Wait()等同步阻塞方法,避免同请求内出现并行的DbContext操作。 - 显式指定DbContext的生命周期,避免其他组件修改默认配置:
services.AddDbContext<Db>(options => { options.UseSqlServer(mySqlConnectionStr); }, contextLifetime: ServiceLifetime.Scoped, optionsLifetime: ServiceLifetime.Scoped);
注意:不要为了规避报错把DbContext注册为Transient,这种方式会导致同一个请求内不同服务注入的DbContext是不同实例,引发实体跟踪不一致、事务失效等更难排查的问题。
内容的提问来源于stack exchange,提问作者Ashwani Bhardwaj
相关产品推荐
相关产品推荐

