1

Time to time I would get the following error and am trying to understand better as to why it happens.

I am seeding users in my configure section and I have the following code.

private WarehouseDBContext _context;

public SeedUsers(WarehouseDBContext context)
{
  _context = context;
 }

public async void SeedAdminUser(IApplicationBuilder app, RoleManager<IdentityRole> 
roleManager, UserManager<ApplicationUser> userMrg)
{
        var user = new ApplicationUser
        {
            UserName = "test@outlook.com",
            NormalizedUserName = "test@outlook.com",
            Email = "test@outlook.com",
            NormalizedEmail = "test@outlook.com",
            FirstName = "test",
            LastName = "user",
            EmailConfirmed = true,
            LockoutEnabled = false,
            SecurityStamp = Guid.NewGuid().ToString()
        };
       

            var password = new PasswordHasher<ApplicationUser>();
            var hashed = password.HashPassword(user, "test123453!");
            user.PasswordHash = hashed;
            var userStore = new UserStore<ApplicationUser>(_context);
           await  userStore.CreateAsync(user);
             await  userStore.AddToRoleAsync(user, "admin");
              var  result =await userMrg.AddToRoleAsync(user, "admin");
 }

This is how I consume the function i shortend the code so didnt have to read all my configure.

public async void Configure(IApplicationBuilder app, IWebHostEnvironment env, 
UserManager<ApplicationUser> userManager,
RoleManager<IdentityRole> roleManager, SeedUsers seed)
{
        seed.SeedRoles();
        seed.SeedAdminUser(app,roleManager, userManager);
        seed.SeedClaimsForSuperAdminAsync(app, roleManager, userManager);
 }

I have declared it as such in the startup.cs

 services.AddTransient<SeedUsers>();
 services.AddTransient<WarehouseDBContext>();

The user is indead added to the user store but the code fails on the userMgr which is being passed in from the startup.cs as such and the AddToRoleAsync fails and does not insert it into the table their is no message that it fails just this error.

So why then am I getting the error. It happens every time on the line above await userStore.CreateAsync(user);?

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

c-sharp-and-swiftui-devni
  • 3,743
  • 4
  • 39
  • 100
  • 1
    Your problem is the use of `async void`. `SeedRoles` doesn't complete before `SeedAdminUser` starts, because that's how `async void` works. You should convert to `async Task` and then `await` those calls. For your next question, see the second part of my answer [here](https://stackoverflow.com/questions/58574899/asp-net-core-3-cannot-resolve-scoped-service-microsoft-aspnetcore-identity-use/58575145#58575145). – Kirk Larkin May 13 '21 at 10:12
  • @KirkLarkin But is it good practise to await items in the configure section i dont see it in other code. – c-sharp-and-swiftui-devni May 13 '21 at 11:18
  • 1
    You can't use `await` in `Configure`, because it must return `void`. The link in my previous comment shows how you can move the seed calls up into `Program` and use `async`/`await` there (see the last code sample). – Kirk Larkin May 13 '21 at 11:22
  • @KirkLarkin it doenst show me how one would access the usermanager or rolemanager in that example as i am using claims in my code – c-sharp-and-swiftui-devni May 13 '21 at 11:34
  • @KirkLarkin never mind thanks i got an example online and got it working thank u – c-sharp-and-swiftui-devni May 13 '21 at 13:14
  • @KirkLarkin one question is there a way in the claims to add an extra field instead of me having Permissions.StockItems.View i would like a two fields for controller and area – c-sharp-and-swiftui-devni May 13 '21 at 13:14

1 Answers1

0

As the comment pointed out, your core problem is due to async void. void is a very unnatural return type for async code, and is mostly just intended to be used for event handlers (or event-handler-like methods).

One of the problems of async void, is that your code cannot determine when the method has completed. In this case, Configure is calling SeedRoles, not (a)waiting for it to complete, calling SeedAdminUser, not (a)waiting for it to complete, and calling SeedClaimsForSuperAdminAsync. Presumably all using the same DbContext, which isn't allowed.

Generally, you should avoid async void (use async Task instead), and use await whenever you call an asynchronous method.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810