Reputation: 21
I'm new to Blazor and don't have much experience working with Tasks, so hopefully I'm just making a foolish mistake. I have an async method that is called via button press, but if the method is called again within 1-2 seconds I get the following exception.
Error: System.InvalidOperationException: A second operation was started on this context 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.
This button is rendered for each row in a Users table. I'm trying to delete multiple user records in quick succession, but receive the above error.
Here is the code for the button press (using AntBlazor)
<Button Type="primary" Danger OnClick="@(async() => await RemoveAsync(user))">Remove User</Button>
And here is the code for the RemoveAsync method.
private async Task RemoveAsync(User user)
{
await UserService.UpdateUserAsync(user);
}
Am I misunderstanding how async/await works? or do I need to make use of Tasks to ensure the action is complete?
Edit:
Heres the UserService.UpdateUserAsync() code
public async Task<bool> UpdateUserAsync(User user)
{
_appDBContext.Users.Update(user);
await _appDBContext.SaveChangesAsync();
return true;
}
Upvotes: 0
Views: 18378
Reputation: 273179
Your code
public async Task<bool> UpdateUserAsync(User user)
{
_appDBContext.Users.Update(user);
await _appDBContext.SaveChangesAsync();
return true;
}
I assume that _appDBContext is injected in the constructor, and that UserService itself is registered as Scoped.
That means that a single _appDBContext lives for the duration of your Form, accumulating tracking data. And because of async
it runs the risk of being re-entered which is your direct problem.
One solution is not to inject a DbContext but a DbContextFactory.
And then it looks like:
public async Task<bool> UpdateUserAsync(User user)
{
using var dbContext = _dbContextFactory.CreateDbContext();
dBContext.Users.Update(user);
var n = await dBContext.SaveChangesAsync();
return n == 1; // just for being accurate
}
Now the context is scoped to each method. Much lighter on memory and you can have many overlapping operations.
Upvotes: 6