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

Blazor Server调用异步UserManager方法时出现Circuit错误

问题

基于Blazor Server开发Web应用,使用IdentityCore管理用户。在Razor组件中用户输入账户数据后,通过依赖注入调用服务创建用户时出现异常。

异常信息

[13:56:45 ERR] An exception occurred while iterating over the results of a query for context type 'ProjectName.Data.Context.DataContext'.
System.InvalidOperationException: A second operation was started on this context instance before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext.
   at Microsoft.EntityFrameworkCore.Infrastructure.Internal.ConcurrencyDetector.EnterCriticalSection()
   at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.Enumerator.MoveNext()
System.InvalidOperationException: A second operation was started on this context instance before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext.
   at Microsoft.EntityFrameworkCore.Infrastructure.Internal.ConcurrencyDetector.EnterCriticalSection()
   at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.Enumerator.MoveNext()
[13:56:45 WRN] Unhandled exception rendering component: Cannot access a disposed object.
System.ObjectDisposedException: Cannot access a disposed object.
   at Microsoft.AspNetCore.Components.RenderTree.ArrayBuilder`1.GrowBuffer(Int32 desiredCapacity)
   at Microsoft.AspNetCore.Components.Rendering.ComponentState.RenderIntoBatch(RenderBatchBuilder batchBuilder, RenderFragment renderFragment, Exception& renderFragmentException)
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.ProcessRenderQueue()
[13:56:45 WRN] Unhandled exception rendering component: Cannot access a disposed object.
Object name: 'UserManager`1'.
System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'UserManager`1'.
   at Microsoft.AspNetCore.Identity.UserManager`1.ThrowIfDisposed()
   at Microsoft.AspNetCore.Identity.UserManager`1.get_SupportsUserLockout()
   at Microsoft.AspNetCore.Identity.UserManager`1.CreateAsync(TUser user)
   at ProjectName.Service.Services.UserService.CreateUser(AppUser user) in C:\Users\erikm\source\repos\ProjectName\ProjectName.Service\Services\UserService.cs:line 66
   at ProjectName.Web.Pages.Employee.CompanyEmployees.CreateHandler(GridCommandEventArgs arg) in C:\Users\erikm\source\repos\ProjectName\ProjectName.Web\Pages\Employee\CompanyEmployees.razor:line 67
   at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
   at Telerik.Blazor.Components.Common.TableGridBase`2.InvokeEventAsync(EventCallback`1 event, TableCommandEventArgs args)
   at Telerik.Blazor.Components.Common.TableGridBase`2.InvokeCreateEventAsync(TableCommandEventArgs args)
   at Telerik.Blazor.Components.Common.TableGridBase`2.InvokeSaveEventAsync(TableCommandEventArgs args)
   at Telerik.Blazor.Components.Common.TableGridBase`2.ExecuteSaveCommandAsync(TableCommandEventArgs args)
   at Telerik.Blazor.Components.Common.TableGridBase`2.ExecuteValidatedCommand(TableCommandEventArgs args)
   at Telerik.Blazor.Components.TelerikGrid`1.ExecuteValidatedCommand(TableCommandEventArgs args)
   at Telerik.Blazor.Components.Common.TableGridBase`2.ExecuteCommand(Object commandArgs)
   at Telerik.Blazor.Components.Common.Grid.GridEditForm`1.FireCommand(String name)
   at Telerik.Blazor.Components.Common.Grid.GridEditForm`1.Update()
   at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
   at Telerik.Blazor.Components.TelerikForm.OnSubmitHandler()
   at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle, ComponentState owningComponentState)

相关代码

Razor组件代码

[Inject]
private IUserService _userService { get; set; }

private async Task CreateHandler(GridCommandEventArgs arg)
{
    if (arg.Item is AppUser user)
    {
        user.CompanyId = Company.Id;
        user.UserName = user.Email;
        await _userService.CreateUser(user);
    }
}

服务层代码

private readonly UserManager<AppUser> _userManager;
private readonly IDbContextFactory<DataContext> _dataContextFactory;
public UserService(UserManager<AppUser> userManager, IDbContextFactory<DataContext> dataContextFactory)
{
    _userManager = userManager;
    _dataContextFactory = dataContextFactory;
}
public async Task<AppUser?> CreateUser(AppUser user)
{
    IdentityResult result = await _userManager.CreateAsync(user);
    return result.Succeeded ? user : null;
}

Program.cs配置代码

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContextFactory<DataContext>(options =>
{
    options.UseSqlServer(connectionString, sqlServerOptionsAction: sqlOptions =>
    {
        sqlOptions.EnableRetryOnFailure();
    });
});

builder.Services.AddIdentity<AppUser, AppRole>(options =>
    {
        options.SignIn.RequireConfirmedAccount = false;
    })
    .AddEntityFrameworkStores<DataContext>()
    .AddDefaultTokenProviders();

builder.Services.AddTransient<IUserService, UserService>();

环境:.NET 7.0,所有相关包均为7.0.0版本,期望在服务层异步创建用户。

解决方案

问题根源在于UserManager依赖的DbContext生命周期与当前配置不匹配:使用AddDbContextFactory注册上下文工厂,但AddEntityFrameworkStores默认使用Scoped生命周期的DbContext实例,Blazor Server组件生命周期较长,易引发并发操作或上下文提前释放问题。

步骤1:修改Identity配置,使用DbContext工厂

替换默认的UserManager注册逻辑,让它通过IDbContextFactory创建独立的上下文实例:

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContextFactory<DataContext>(options =>
{
    options.UseSqlServer(connectionString, sqlServerOptionsAction: sqlOptions =>
    {
        sqlOptions.EnableRetryOnFailure();
    });
});

builder.Services.AddIdentity<AppUser, AppRole>(options =>
    {
        options.SignIn.RequireConfirmedAccount = false;
    })
    .AddDefaultTokenProviders();

// 自定义注册UserManager,使用DbContextFactory创建上下文
builder.Services.AddScoped<UserManager<AppUser>>(sp =>
{
    var contextFactory = sp.GetRequiredService<IDbContextFactory<DataContext>>();
    var userStore = new UserStore<AppUser, AppRole, DataContext>(contextFactory.CreateDbContext());
    var passwordHasher = sp.GetRequiredService<IPasswordHasher<AppUser>>();
    var userValidators = sp.GetServices<IUserValidator<AppUser>>();
    var passwordValidators = sp.GetServices<IPasswordValidator<AppUser>>();
    var keyNormalizer = sp.GetRequiredService<ILookupNormalizer>();
    var errors = sp.GetRequiredService<IdentityErrorDescriber>();
    var services = sp.GetRequiredService<IServiceProvider>();
    var logger = sp.GetRequiredService<ILogger<UserManager<AppUser>>>();

    return new UserManager<AppUser>(
        userStore, null, passwordHasher, userValidators, passwordValidators, 
        keyNormalizer, errors, services, logger);
});

builder.Services.AddTransient<IUserService, UserService>();

步骤2:验证异步操作正确性

确保所有异步方法都正确使用await(当前Razor组件和服务层代码已满足此要求),避免因未等待导致的上下文状态异常。

补充说明

  • DbContext线程不安全,必须保证每个操作使用独立实例,IDbContextFactory是Blazor Server中管理上下文的推荐方式。
  • 自定义UserManager注册逻辑后,每次调用UserManager方法都会获取全新的上下文实例,从根本上避免并发操作和上下文释放问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 09:25:37