0

I'm getting an "ConcurrencyFailure" with "Optimistic concurrency failure, object has been modified." message after trying to save a new object to a list.

I tried the methods with the row version and [ConcurrencyCheck] in the property but still no success.

var user = await _userManager.FindByIdAsync(User.FindFirstValue(ClaimTypes.NameIdentifier));

user!.Websites.Add(website);

IdentityResult result = await _userManager.UpdateAsync(user);

if(result.Succeeded)
{
    return Ok(user.Websites);
}

I understand the concept of the optimistic failure and pessimistic one but i still can't manage to update the user.

I get the changes in the context but not to the database.

1 Answers1

1

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:

  1. Do everything synchronously.
  2. 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);
}
MrC aka Shaun Curtis
  • 19,075
  • 3
  • 13
  • 31