NET8, Blazor and Microsoft Identity: error context instance before a previous operation completed

153 Views Asked by At

I'm creating a new website with Blazor and the NET8 framework using the boilerplate from Visual Studio 2022 Preview. The source code of this test is on GitHub.

As in my other post, I have already changed the ApplicationUser adding a new properties such as

public string? FirstName { get; set; }
public string? LastName { get; set; }

Now, in the file Index.razor under Components > Pages > Account > Manage, I like to allow the users to change or add those values.

enter image description here

The important thing is that on the left side, in the _NavManu.razor, there is a place where I display the username or the FirstName like that

<AuthorizeView>
    <Authorized>
        <div class="nav-item px-3">
            <NavLink class="nav-link" href="/Account/Manage">
                <span class="bi bi-person-fill" aria-hidden="true"></span> 
                @(usermanager.GetUserAsync(context.User).Result.FirstName ?? 
                  context.User.Identity?.Name)
            </NavLink>
        </div>
    </Authorized>
</AuthorizeView>

in the NavMenu.razor I added at the top of the page

@inject UserManager<ApplicationUser> usermanager

Now, when I try to save a new value for the FirstName for example, I invoke

if (Input.FirstName != _firstname)
{
    _user.FirstName = Input.FirstName;
    await UserManager.UpdateAsync(_user);
}

Immediately, I get an error in the NavMenu.razor

InvalidOperationException: A second operation was started on this context instance before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext. For more information on how to avoid threading issues with DbContext, see https://go.microsoft.com/fwlink/?linkid=2097913.

The same code is working in the container of a NET7 Blazor application without issues.

enter image description here

Is there a way that I can fix it?

Update

After the message from H H, I added in the NavMenu.razor this code

@code {
    private string? Username;

    protected override async Task OnInitializedAsync()
    {
        var authState = await 
            AuthenticationStateProvider.GetAuthenticationStateAsync();
        var user = authState.User;

        if (user.Identity.IsAuthenticated)
        {
            var fulldetails = await usermanager.GetUserAsync(user);
            Username = fulldetails.FirstName ?? user.Identity.Name;
        }
    }
}

With this, I can save an update as I can see here.

enter image description here

but if I click on any link, immediately I get this error

InvalidOperationException: A second operation was started on this context instance before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext. For more information on how to avoid threading issues with DbContext, see https://go.microsoft.com/fwlink/?linkid=2097913. Microsoft.EntityFrameworkCore.Infrastructure.Internal.ConcurrencyDetector.EnterCriticalSection()

enter image description here

1

There are 1 best solutions below

1
On

The main problem probably is @(usermanager.GetUserAsync(context.User).Result.FirstName ...

The (generated) BuildRenderTree method is not async and using .Result is asking for deadlocks.

When you await inside your SaveAsync() method a Render will happen (*) and that is where the conflict occurs.

The solution is to establish the userName in OnInitalizedAsync , not in the markup.

(*) I have to check that for SSR