Blazor和EF core -不同的线程同时使用同一个DbContext实例- Problem
我试图导航到一个Blazor组件,但该组件加载两次。
@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>
此代码将执行两次。
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)
{
}
}
}
我已经更改了_Host。根据:
<component type="typeof(App)" render-mode="Server" />
Blazor渲染内容两次
但是我将以错误结束:
错误:系统。InvalidOperationException:已经有一个打开的数据读取器与此连接相关联,必须先关闭它。,在Microsoft.Data.SqlClient.SqlCommand。你们的在c。b__208_0(Task
1 result) at System.Threading.Tasks.ContinuationResultTaskFromResultTask
. innerinvoke ())在System.Threading.ExecutionContext。RunInternal(ExecutionContext ExecutionContext, ContextCallback, callback, Object state)
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();
如果我在组件加载后手动调用该方法没有问题,但我当然希望在组件加载时自动加载和显示数据。
<button type="submit" class="btn btn-primary" @onclick="GetUserInfo">Ladda</button>
@code {
private async Task GetUserInfo()
{
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)
{
}
}
}
}
更新1
编辑UserService.cs
private readonly IDbContextFactory<MyDbContext> _factory;
public UserService(IDbContextFactory<MyDbContext> factory)
{
_factory = factory;
}
这删除了错误,但组件仍然触发OnInitializedAsync()
两次。这将第一次显示数据,但在导航到另一个页面并返回后,var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
为空。
更新2
导航或更新时丢失数据的问题是保存AuthenticationState的错误实现。我安装了blazered。在LocalStorage中保存状态。OnInitializedAsync()
仍然触发两次
更新3
我不确定我做了什么,但修复AuthenticationState和重新启动VS解决了我的问题!
你的问题可能不是直接相关的。
让我们先处理DbContext问题。
您试图同时在两个操作中使用相同的DbContext
。这是异步操作中的一个常见问题。您已经将IDbContextFactory
加载到DI中,您只需要开始使用它来创建事务/工作单元上下文。
这是你的UserService
:
public class UserService
{
private readonly IDbContextFactory _factory;
public UserService(IDbContextFactory factory)
{
_factory = factory;
}
//...
public async ValueTask SomeWorkAsync()
{
using var dbContext = _factory.CreateDbContext();
// await do something with dbContext
}
}
加载两次时,OnInitializedAsync
加载两次可能有三个原因:
- 您正在重新加载SPA,或者这是您的启动页面。
- 您正在手动调用它。
- 正在加载组件的多个实例。
你在哪里导航到MySettings
,如何导航?
更新你需要确定代码是加载了两次还是加载了两个不同的组件。
添加到MySettings
并检查
@code {
public readonly Guid ComponentUid = Guid.NewGuid();
protected override void OnInitialized()
{
Debug.WriteLine($"XXX OnInitialized called on Component Uid = {ComponentUid.ToString()}");
}
protected override Task OnInitializedAsync()
{
Debug.WriteLine($"XXX OnInitializedAsync called on Component Uid = {ComponentUid.ToString()}");
//...
}
}
检查输出