Blazor和EF core -不同的线程并发使用同一个DbContext实例



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(Task1 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加载两次可能有三个原因:

  1. 您正在重新加载SPA,或者这是您的启动页面。
  2. 您正在手动调用它。
  3. 正在加载组件的多个实例。

你在哪里导航到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()}");
//...
}
}

检查输出

相关内容

  • 没有找到相关文章

最新更新