1

In my Blazor server side app with EF Core and MS Identity, I ran into a A second operation was started on this context instance before a previous operation completed error. I could trace it to MS Identity UserManager, who seems to use the same DbContext for all Blazor components.

My parent component and some child components use UserManager. Since - according to my understanding - each component runs in a separte thread, each component should use a different DbContext to prevent such concurrency issues.

I use dependency injection to obtain the UserManager in each component, and unfortunately, I do not know who to check which DbContext is used by UserManager, but the hash code of UserManager in each component instance is the same, strongly indicating that multiple Blazor components share the same DbContext, which causes the concurrency issue.

As a workaround and as a test if my conclusions are correct, I use a SemaphoreSlim to synchronize the use of UserManager across the differnt Blazor components and the concurrency issue goes away. So I am confident that the injected UserManagers share a single DbContext.

I register a 'fresh' instance of the DbContext for MS Identity using the factory accoring to this SO post

services.AddTransient<myContext>(p => p.GetRequiredService<IDbContextFactory<myContext>>().CreateDbContext());

and here I expect the problem: MS Identity only gets, respectively uses, a DbContext and not a DbContextFactory, so it has to use the same DbContext when injecting a UserManager.

Is there a way to make UserManager use a 'fresh' DbContext each time it is injected?

Qrt
  • 389
  • 3
  • 16
  • I filed this [issue](https://github.com/dotnet/aspnetcore/issues/43687) but it was closed without a solution. – Qrt Sep 23 '22 at 11:19

1 Answers1

0

I found something that might help. I was experiencing this issue while trying to inject the usermanager to get user roles for a user table. I found this post. I used the inherit component base then Service.GetUserRolesAsync(user)

I was able to loop through the user manager and get all the roles without causing a concurrency exception.

here is a copy of the contents of the post.

@page "/"
@inherits OwningComponentBase<UserManager<IdentityUser>>
<h1>Hello, world!</h1>

number of users: @Users.Count()
<button @onclick="@(async () => await Add())">click me. I work if you use Sqlite</button>

<ul>
@foreach(var user in Users) 
{
    <li>@user.UserName</li>
}
</ul>

@code {
    IQueryable<IdentityUser> Users;

    protected override void OnInitialized()
    {
        Users = Service.Users;
    }

    public async Task Add()
    {
        await Service.CreateAsync(new IdentityUser { UserName = $"test_{Guid.NewGuid().ToString()}" });
    }
}

Edit: My answer may be flawed. But here is some more documentation.