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

依赖注入UserManager引发并发问题:如何使其使用不同DbContext?

解决Blazor Server中UserManager共享DbContext导致的并发操作错误

核心问题原因

Blazor Server的Scoped服务与用户电路(Circuit)绑定,默认UserManager<TUser>以Scoped方式注册,同一用户的所有组件会共享同一个UserManager实例,进而复用其依赖的DbContext。当多个组件同时通过UserManager操作数据库时,就会触发"A second operation was started on this context instance before a previous operation completed"错误。

解决方案:让UserManager每次注入使用全新DbContext

步骤1:自定义UserStore,依赖DbContextFactory

创建自定义UserStore,通过IDbContextFactory<myContext>创建全新的DbContext实例,替代直接依赖已有DbContext的逻辑:

using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using System.Collections.Generic;
using System.Linq;

public class CustomUserStore : UserStore<IdentityUser>
{
    public CustomUserStore(IDbContextFactory<myContext> contextFactory,
        IOptions<IdentityOptions> optionsAccessor = null,
        IPasswordHasher<IdentityUser> passwordHasher = null,
        IEnumerable<IUserValidator<IdentityUser>> userValidators = null,
        IEnumerable<IPasswordValidator<IdentityUser>> passwordValidators = null,
        ILookupNormalizer keyNormalizer = null,
        IdentityErrorDescriber errors = null,
        IServiceProvider services = null,
        ILogger<UserStore<IdentityUser>> logger = null)
        : base(contextFactory.CreateDbContext(), optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
    {
    }
}

步骤2:调整服务注册逻辑

在Program.cs中,先注册DbContextFactory,再替换默认UserStore为自定义实现,并将UserManager<TUser>注册为Transient,确保每次注入都获取全新实例:

// 注册DbContext工厂
builder.Services.AddDbContextFactory<myContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

// 注册自定义UserStore为Transient
builder.Services.AddTransient<IUserStore<IdentityUser>, CustomUserStore>();

// 添加Identity服务,并将UserManager注册为Transient
builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddUserManager<UserManager<IdentityUser>>()
    .AddEntityFrameworkStores<myContext>();

步骤3:验证效果

修改后,每个组件注入的UserManager都是全新实例,且每个实例使用的DbContext都是工厂创建的新实例,不会再出现并发操作同一DbContext的问题,无需再用SemaphoreSlim做同步。

补充说明

  • 直接将DbContext注册为Transient无法解决问题:因为默认UserManager是Scoped,会在其生命周期内复用同一个Transient的DbContext实例。
  • 将UserManager注册为Transient是关键:确保每次组件注入时都能拿到新的UserManager,自定义UserStore则保证每个UserManager使用的DbContext是全新的。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 02:54:37