C
C#•12h ago
Manicraft1001

Handle DbContext and IDbContextFactory<T> lifetime in Blazor InteractiveServer

Hi, I have some trouble getting EfCore to run properly with Blazor components and pages. I scaffolded a project using the Identity UI template and implemented business logic. I wanted to deploy a first version on production but I'm stuck with flaky instances of the DbContext. For example, the autogenerated Components/Account/Pages/Manage/Index.razor page contains the following initialization method:
protected override async Task OnInitializedAsync()
{
_user = await UserAccessor.GetRequiredUserAsync(HttpContextAccessor.HttpContext!);
_username = await UserManager.GetUserNameAsync(_user);
_phoneNumber = await UserManager.GetPhoneNumberAsync(_user);

Input.PhoneNumber ??= _phoneNumber;
Input.IsAdult = _user.IsAdult;
}
protected override async Task OnInitializedAsync()
{
_user = await UserAccessor.GetRequiredUserAsync(HttpContextAccessor.HttpContext!);
_username = await UserManager.GetUserNameAsync(_user);
_phoneNumber = await UserManager.GetPhoneNumberAsync(_user);

Input.PhoneNumber ??= _phoneNumber;
Input.IsAdult = _user.IsAdult;
}
In the same razor file, I inject the factory for the DB context at the top;
@inject IDbContextFactory<ApplicationDbContext> DbContextFactory
@inject IDbContextFactory<ApplicationDbContext> DbContextFactory
I then update some properties in a method below:
if (Input.IsAdult != _user.IsAdult)
{
await using (ApplicationDbContext dbContext = DbContextFactory.CreateDbContext())
{
ApplicationUser dbUser = await dbContext.Users.SingleAsync(dbUser => dbUser.Id == _user.Id);
dbUser.IsAdult = Input.IsAdult;
await dbContext.SaveChangesAsync();
}
}
if (Input.IsAdult != _user.IsAdult)
{
await using (ApplicationDbContext dbContext = DbContextFactory.CreateDbContext())
{
ApplicationUser dbUser = await dbContext.Users.SingleAsync(dbUser => dbUser.Id == _user.Id);
dbUser.IsAdult = Input.IsAdult;
await dbContext.SaveChangesAsync();
}
}
This results in the following stacktrace:
An unhandled exception has occurred while executing the request.
System.InvalidOperationException: A second operation was started on this context instance before a previous operation completed. This is usuall
y 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.
at Microsoft.EntityFrameworkCore.Infrastructure.Internal.ConcurrencyDetector.EnterCriticalSection()
at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()
at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)
at XXXXXXXXXXXXX.Components.Account.IdentityUserAccessor.GetRequiredUserAsync(HttpContext context) in C:\XXXXXXXXXXXXX\Components\Account\IdentityUserAccessor.cs:line 12
at XXXXXXXXXXXXX.Components.Account.Pages.Manage.Index.OnInitializedAsync() in C:\XXXXXXXXXXXXX\Components\Account\Pages\Manage\Index.razor:line 63
An unhandled exception has occurred while executing the request.
System.InvalidOperationException: A second operation was started on this context instance before a previous operation completed. This is usuall
y 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.
at Microsoft.EntityFrameworkCore.Infrastructure.Internal.ConcurrencyDetector.EnterCriticalSection()
at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()
at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)
at XXXXXXXXXXXXX.Components.Account.IdentityUserAccessor.GetRequiredUserAsync(HttpContext context) in C:\XXXXXXXXXXXXX\Components\Account\IdentityUserAccessor.cs:line 12
at XXXXXXXXXXXXX.Components.Account.Pages.Manage.Index.OnInitializedAsync() in C:\XXXXXXXXXXXXX\Components\Account\Pages\Manage\Index.razor:line 63
I've read and understood this official guide to work with EfCore in Blazor apps, but even though I never use DbContext directly and always create an instance on the fly using IDbContextFactory<AppDbContext>, I still get this error. Here is the content of the IdentityUserAccessor:
internal sealed class IdentityUserAccessor(
UserManager<ApplicationUser> userManager,
IdentityRedirectManager redirectManager)
{
public async Task<ApplicationUser> GetRequiredUserAsync(HttpContext context)
{
var user = await userManager.GetUserAsync(context.User);

if (user is null)
{
redirectManager.RedirectToWithStatus("Account/InvalidUser",
$"Error: Unable to load user with ID '{userManager.GetUserId(context.User)}'.", context);
}

return user;
}
}
internal sealed class IdentityUserAccessor(
UserManager<ApplicationUser> userManager,
IdentityRedirectManager redirectManager)
{
public async Task<ApplicationUser> GetRequiredUserAsync(HttpContext context)
{
var user = await userManager.GetUserAsync(context.User);

if (user is null)
{
redirectManager.RedirectToWithStatus("Account/InvalidUser",
$"Error: Unable to load user with ID '{userManager.GetUserId(context.User)}'.", context);
}

return user;
}
}
Why is this happening? What am I doing wrong? Thanks for your help in advance! 🚀🙌
ASP.NET Core Blazor with Entity Framework Core (EF Core)
Learn how to use Entity Framework Core (EF Core) in Blazor apps.
ASP.NET Core Blazor authentication and authorization
Learn about Blazor authentication and authorization scenarios.
1 Reply
Manicraft1001
Manicraft1001OP•11h ago
After much troubleshooting, I found the solution. I used UserManager<ApplicationUser> and RoleManager<IIdentityRole> in both layouts and components, leading to simulatneous calls. Using the AuthenticationStateProvider in layouts fixed the issue.

Did you find this page helpful?