问题描述
我的目标
我想创建一个新的 IdentityUser 并显示已通过同一个 Blazor 页面创建的所有用户.该页面有:
I want to create a new IdentityUser and show all the users already created through the same Blazor page. This page has:
- 一个通过你的表单将创建一个 IdentityUser
- 第三方的网格组件 (DevExpress Blazor DxDataGrid),显示所有使用 UserManager.Users 属性的用户.此组件接受 IQueryable 作为数据源.
问题
当我通过表单(1)创建一个新用户时,会出现以下并发错误:
When I create a new user through the form (1) I will get the following concurrency error:
InvalidOperationException:在上一个操作完成之前,在此上下文中启动了第二个操作.不保证任何实例成员都是线程安全的.
我认为问题与 CreateAsync(IdentityUser user) 和 UserManager.Users 指的是相同的 DbContext
I think the problem is related to the fact that CreateAsync(IdentityUser user) and UserManager.Users are referring the same DbContext
这个问题与第三方的组件无关,因为我重现了同样的问题,用一个简单的列表替换它.
The problem isn't related to the third-party's component because I reproduce the same problem replacing it with a simple list.
重现问题的步骤
- 使用身份验证创建一个新的 Blazor 服务器端项目
使用以下代码更改 Index.razor:
- create a new Blazor server-side project with authentication
change Index.razor with the following code:
@page "/"
<h1>Hello, world!</h1>
number of users: @Users.Count()
<button @onclick="@(async () => await Add())">click me</button>
<ul>
@foreach(var user in Users)
{
<li>@user.UserName</li>
}
</ul>
@code {
[Inject] UserManager<IdentityUser> UserManager { get; set; }
IQueryable<IdentityUser> Users;
protected override void OnInitialized()
{
Users = UserManager.Users;
}
public async Task Add()
{
await UserManager.CreateAsync(new IdentityUser { UserName = $"test_{Guid.NewGuid().ToString()}" });
}
}
我注意到的
- 如果我将实体框架提供程序从 SqlServer 更改为 Sqlite,则永远不会显示错误.
系统信息
- ASP.NET Core 3.1.0 Blazor 服务器端
- Entity Framework Core 3.1.0 基于 SqlServer 提供程序
我已经看到的
- Blazor 一秒操作在上一个操作完成之前在此上下文中开始:建议的解决方案对我不起作用,因为即使我将 DbContext 范围从 Scoped 更改为 Transient我仍然使用相同的 UserManager 实例,它包含相同的 DbContext 实例
- StackOverflow 上的其他人建议为每个请求创建一个新的 DbContext 实例.我不喜欢这个解决方案,因为它违反了依赖注入原则.无论如何,我无法应用此解决方案,因为 DbContext 包含在 UserManager 中
- 创建 DbContext 的生成器:这个解决方案与之前的解决方案非常相似.
- 将 Entity Framework Core 与 Blazor 结合使用
- Blazor A second operation started on this context before a previous operation completed: the solution proposed doesn't work for me because even if I change my DbContext scope from Scoped to Transient I still using the same instance of UserManager and its contains the same instance of DbContext
- other guys on StackOverflow suggests creating a new instance of DbContext per request. I don't like this solution because it is against Dependency Injection principles. Anyway, I can't apply this solution because DbContext is wrapped inside UserManager
- Create a generator of DbContext: this solution is pretty like the previous one.
- Using Entity Framework Core with Blazor
为什么我要使用 IQueryable
我想传递一个 IQueryable 作为我第三方组件的数据源,因为它可以直接对查询应用分页和过滤.此外 IQueryable 对 CUD 敏感操作.
I want to pass an IQueryable as a data source for my third-party's component because its can apply pagination and filtering directly to the Query. Furthermore IQueryable is sensitive to CUD operations.
推荐答案
UPDATE (08/19/2020)
UPDATE (08/19/2020)
在这里您可以找到 有关如何同时使用 Blazor 和 EFCore 的文档
更新 (07/22/2020)
UPDATE (07/22/2020)
EFCore 团队在 Entity Framework Core .NET 5 Preview 7 中引入了 DbContextFactory
EFCore team introduces DbContextFactory inside Entity Framework Core .NET 5 Preview 7
[...] 这种解耦对于 Blazor 应用程序非常有用,推荐使用 IDbContextFactory,但在其他场景中也可能有用.
如果您有兴趣,可以在 宣布 Entity Framework Core EF Core 5.0 Preview 7
If you are interested you can read more at Announcing Entity Framework Core EF Core 5.0 Preview 7
更新 (07/06/2020)
UPDATE (07/06/2020)
Microsoft 发布了一个关于 Blazor(两种型号)的有趣的新视频和实体框架核心.请看19:20,他们在讨论如何用EFCore管理并发问题
Microsoft released a new interesting video about Blazor (both models) and Entity Framework Core. Please take a look at 19:20, they are talking about how to manage concurrency problem with EFCore
我向 Daniel Roth BlazorDeskShow - 2:24:20 询问了这个问题以及它设计上似乎是 Blazor 服务器端 问题.DbContext 默认生命周期设置为 Scoped
.因此,如果您在同一页面中至少有两个组件尝试执行 async 查询,那么我们将遇到异常:
I asked Daniel Roth BlazorDeskShow - 2:24:20 about this problem and it seems to be a Blazor Server-Side problem by design.DbContext default lifetime is set to Scoped
. So if you have at least two components in the same page which are trying to execute an async query then we will encounter the exception:
InvalidOperationException:在上一个操作完成之前,在此上下文中启动了第二个操作.不保证任何实例成员都是线程安全的.
关于这个问题有两个解决方法:
- (A) 将 DbContext 的生命周期设置为 Transient
services.AddDbContext<ApplicationDbContext>(opt =>
opt.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")), ServiceLifetime.Transient);
- (B) 正如 Carl Franklin 建议的那样(在我的问题之后):创建一个带有静态方法的单例服务,该方法返回
DbContext
的新实例. - (B) as Carl Franklin suggested (after my question): create a singleton service with a static method which returns a new instance of
DbContext
. - 拥有相同
UserManager
实例的两个组件,该实例包含一个瞬态DbContext
. - two components that own the same
UserManager<TUser>
instance which contains a transientDbContext
. - 我不直接使用
UserManager
,而是通过IServiceProvider
创建它的一个新实例,然后它就可以工作了.我仍在寻找一种方法来更改 UserManager 的生命周期 而不是使用IServiceProvider
. - I don't use
UserManager<TUser>
directly instead, I create a new instance of it throughIServiceProvider
and then it works. I am still looking for a method to change the UserManager's lifetime instead of usingIServiceProvider
.
无论如何,每个解决方案都有效,因为它们创建了 DbContext
的新实例.
anyway, each solution works because they create a new instance of DbContext
.
我的问题与 DbContext
并不严格相关,而是与 UserManager
相关,它具有 Scoped
生命周期.将 DbContext 的生命周期设置为 Transient
并没有解决我的问题,因为 ASP.NET Core 在我第一次打开会话时创建了一个 UserManager
的新实例,它直到我不关闭它为止.此 UserManager
位于同一页面上的两个组件内.然后我们遇到了之前描述的相同问题:
My problem wasn't strictly related to DbContext
but with UserManager<TUser>
which has a Scoped
lifetime. Set DbContext's lifetime to Transient
didn't solve my problem because ASP.NET Core creates a new instance of UserManager<TUser>
when I open the session for the first time and it lives until I don't close it. This UserManager<TUser>
is inside two components on the same page. Then we have the same problem described before:
目前,我用另一种解决方法解决了这个问题:
Currently, I solved this problem with another workaround:
tips:注意服务的生命周期
这是我学到的.不知道对不对.
This is what I learned. I don't know if it is all correct or not.
这篇关于使用 Entity Framework Core 的 Blazor 并发问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!