Concurrency problems are caused when two processes are trying to use the same [DbContext] resource at the same time. Blazor is inherently asynchronous, so the chances of concurrency problems are pretty high. The problem is solved by using a DbContext Factory and Unit of Work contexts.
However, that's not so easy with the UserManager without a lot of coding. Someone may have a quick answer to that.
You're probably trying to do something like the code below. You haven't given much away about what the rest of the code looks like, so I'm Educated Guessing here. I may be wide of the mark!
The problem in the below example is that UpdateUser
yields on await _UserManager.UpdateAsync(_user)
[that's one open connection on the DbContext], and the UI handler calls StateHasChanged
which then calls var user in _UserManager.Users
: an attempt to use a context that's already in use.
You can either:
- Do everything synchronously.
- Do what I've show which is override the
ComponentBase
UI handler HandleEventAsync
with a version that doesn't call StateHasChanged
. You do it manually when you need to: in the code after UpdateUser
finishes with the context.
@page "/"
@implements IHandleEvent
@using Microsoft.AspNetCore.Identity;
@inject UserManager<IdentityUser> _UserManager
<h3>UserManager</h3>
@foreach( var user in _UserManager.Users )
{
<div class=" m-1 p-1 bg-dark text-white mb-3">
<div>
User Name: @user.UserName
</div>
<div>
Id: @user.Id
</div>
<div>
Phone: @user.PhoneNumber
</div>
</div>
}
<input class="form-control mb-3" @bind=this.phone />
<div class="m-2 text-end">
<button class="btn btn-primary" @onclick=this.UpdateUser>Update User's Phone Number</button>
</div>
@code {
private string? phone;
private IdentityUser? _user;
private async Task UpdateUser()
{
_user = _UserManager.Users.First();
if (_user is not null)
{
_user.PhoneNumber = this.phone;
var result = await _UserManager.UpdateAsync(_user);
}
// Manual refresh after the _UserManager action completes
StateHasChanged();
}
// This overrides the standard double render handler
// with a simple handler that does no automatic calls to StateHasChanged.
// It's up to you if and when you call it
async Task IHandleEvent.HandleEventAsync(EventCallbackWorkItem callback, object? arg)
=> await callback.InvokeAsync(arg);
}