I have read the following questions before asking:
Following the advice given in the answer to these questions I have checked that my async
call 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 ?
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");
This problem is caused because the context was unable to perform first operation and got stuck. Most common reasons are