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
相关产品推荐
相关产品推荐

