SignInManager gives "a second operation started on this context"

asp.net-core-2.0 asp.net-identity entity-framework-core

Question

I have read the following questions before asking:

Following the advice given in the answer to these questions I have checked that my asynccall had its await directive and I verified that nothing was configured as static in the Startup.cs

I get the following error

InvalidOperationException: A second operation started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe.

when the following code is called twice in a row.

code reduced to the minimum needed to understand

public async Task<IActionResult> Login(LoginViewModel model)
{

    /* [...] */

    var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, isPersistent: model.RememberMe, lockoutOnFailure: false);

    if (result.Succeeded)
    {
        /* [...] */
    }

    /* [...] */
}

the sign-in manager is declared as private readonly SignInManager<User> _signInManager; and is obtained via dependency injection through the constructor.

The DbContext and Identity context are configured as services in the code below. Nothing is declared as static as far as I know.

services.AddDbContext<RelaxationDbContext>(options =>
    options.UseSqlServer(Configuration.GetConnectionString("RelaxationDbContext")));

services.AddIdentity<User, AppIdentityRole>()
    .AddEntityFrameworkStores<RelaxationDbContext>()
    .AddDefaultTokenProviders()
    .AddErrorDescriber<FrenchIdentityErrorDescriber>(); 

Why am I getting an error there ? Is there a bug in the framework or have I done something wrong ? How can I prevent this error from occurring in the future ?

1
0
6/13/2018 6:52:28 AM

Accepted Answer

Turns out, since I couldn't figure out how to seed a test user in the in my DbContext using HasData because I didn't have any mean of hashing the password I ended up generating the user in the constructor of the main controller using the following code

var findByEmailAsync = _userManager.FindByEmailAsync("foo@foo.foo");
if (findByEmailAsync.Result == null)
    _userManager?.CreateAsync(new User()
    {
        Id = "37846734-172e-4149-8cec-6f43d1eb3f60",
        Email = "foo@foo.foo",
        UserName = "foo@foo.foo"
    }, "Password_1");

Then, since I am currently working on the "change email" feature, after changing the email and confirming the change the user tried to log back in. And hell, that's where the shenanigans came in !

Since the user had changed email the FindByEmailAsync method didn't find it in the database leading to a call to CreateAsync. For some reason this call would sometimes run properly and sometimes crash ... but most of the time the app would crash on the next call to the _userManager which happend almost always during a login attempt.

I do not really understand why it is THIS error that is returned instead of a duplicate id one ...

As a temporary fix until I figure out how to seed users in the database I've replace my code's first line with this one var findByEmailAsync = _userManager.FindByIdAsync("37846734-172e-4149-8cec-6f43d1eb3f60");

0
6/13/2018 7:46:34 PM

Popular Answer

This problem is caused because the context was unable to perform first operation and got stuck. Most common reasons are

  1. You are using context with async but your operation is taking longer to complete.
  2. Connection string is wrong due to which operation is taking longer to complete.


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow