Blazor与EF Core:DbContext多线程并发及组件重复加载问题
Blazor组件导航时加载两次,
OnInitializedAsync执行两次及相关数据库错误的解决过程 问题现象
- 导航至
/MySettings组件时,组件重复加载两次,OnInitializedAsync()方法随之执行两次 - 将
_Host.cshtml的渲染模式修改为Server(<component type="typeof(App)" render-mode="Server" />)后,触发数据库连接错误:
Error: System.InvalidOperationException: There is already an open DataReader associated with this Connection which must be closed first.
at Microsoft.Data.SqlClient.SqlCommand.<>c.b__208_0(Task1 result) at System.Threading.Tasks.ContinuationResultTaskFromResultTask2.InnerInvoke()
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
相关代码
组件代码(MySettings.razor)
@page "/MySettings" @using eKurser_Blazor.Data.Services; @using eKurser_Blazor.DataDB; @inject UserService UserService @inject AuthenticationStateProvider AuthenticationStateProvider <h1>Get profile</h1> @if (ErrorMessage != null) { <div class="alert alert-danger">@ErrorMessage</div> } <form> <div class="form-group"> <label for="firstName">Fname</label> <input type="text" class="form-control" id="firstName" @bind="@Fname" @value="@Fname"> </div> <div class="form-group"> <label for="lastName">Lname</label> <input type="text" class="form-control" id="lastName" @bind="@Lname" @value="@Lname"> </div> <div class="form-group"> <label for="email">E-mail</label> <input type="email" class="form-control" id="email" @bind="@Email" @value="@Email"> </div> </form>
重复执行的OnInitializedAsync代码
protected override async Task OnInitializedAsync() { var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); var user = authState.User; string userIdClaim = user.FindFirst("UserID")?.Value; if (!string.IsNullOrEmpty(userIdClaim)) { var _user = await UserService.GetUserByIdAsync(userIdClaim); if (_user != null) { } } }
UserService.cs代码
public class UserService { private readonly MyDbContext _dbContext; public UserService(MyDbContext dbContext) { _dbContext = dbContext; } public async Task<TblUser> AuthenticateAsync(string email, string password) { if (string.IsNullOrEmpty(email) || string.IsNullOrEmpty(password)) { return null; } TblUser user = null; try { user = await _dbContext.TblUsers.SingleOrDefaultAsync(x => x.Email == email); } catch (Exception ex) { Console.WriteLine($"Error: {ex.Message}"); return null; } if (user == null) { } if (user == null || !PasswordHash.ValidatePassword(password, user.Pwd)) { return null; } return user; } public async Task<TblUser> GetUserByIdAsync(string userId) { if (string.IsNullOrEmpty(userId)) { return null; } var user = await _dbContext.TblUsers.SingleOrDefaultAsync(x => x.UserId == userId); return user; } private bool UserExists(string userId) { return _dbContext.TblUsers.Any(e => e.UserId == userId); } }
Program.cs配置代码
builder.Services.AddRazorPages(); builder.Services.AddServerSideBlazor(); builder.Services.AddScoped<UserService>(); builder.Services.AddScoped<CustomAuthenticationStateProvider>(); builder.Services.AddScoped<AuthenticationStateProvider>(provider => provider.GetRequiredService<CustomAuthenticationStateProvider>()); var connString = builder.Configuration.GetConnectionString("DefaultConnection"); builder.Services.AddDbContextFactory<MyDbContext>((DbContextOptionsBuilder option ) => option.UseSqlServer(connString)); // Add services to the container. builder.Services.AddControllersWithViews();
解决步骤与最终结果
- 更新1:改用
IDbContextFactory解决了数据库连接错误,但组件重复加载、导航后认证状态为空的问题仍存在 - 更新2:通过Blazored.LocalStorage修复认证状态存储,但组件重复加载问题未解决
- 更新3:修复认证状态逻辑并重启Visual Studio后,所有问题均得到解决
内容的提问来源于stack exchange,提问作者Jonas Johansson
相关产品推荐
相关产品推荐

