Search code examples
c#asp.net-mvcasp.net-identityowin

How to fix 'A second operation started on this context' error on SignInManager.PasswordSignIn()


I have an MVC system that uses .net Identity login, however, when two separate users log into the system at roughly the same time, the user who clicks the button last gets an error saying;

One or more errors occurred. A second operation started on this context before a previous asynchronous operation completed. Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread-safe.

This error happens on the SignInManager.PasswordSignInAsync() line but I am stuck as to what I've done wrong.

Admittedly, I am not too familiar with OWIN, but if anyone has any clues as to what I can do to stop this happening that would be much appreciated.

To cover off a few things, I have already tried calling PasswordSignIn instead of PasswordSingInAsync and I've tried awaiting the call but its the same story - I believe this is because it's two entirely separate requests.

In brief, my code is set up as follows;

LoginController.cs

public ActionResult Login(LoginModel model)
{
    _identityManagement.SignInManager.PasswordSignInAsync(model.Username, model.Password, model.PersistentLogin, false);

    //We do some stuff here but it fails before this point so code removed.
    return null;
}

IdentityManagement.cs

public class IdentityManagement
{
    public ApplicationSignInManager SignInManager
    {
        get
        {
            return HttpContext.Current.GetOwinContext().Get<ApplicationSignInManager>();
        }
    }
}

ApplicationSignInManager.cs

public class ApplicationSignInManager : SignInManager<SystemUser, string>
{
    public ApplicationSignInManager(ApplicationUserManager userManager, IAuthenticationManager authenticationManager) : base(userManager, authenticationManager)
    {
    }

    public static ApplicationSignInManager Create(IdentityFactoryOptions<ApplicationSignInManager> options, IOwinContext context)
    {
        return new ApplicationSignInManager(context.GetUserManager<ApplicationUserManager>(), context.Authentication);
    }
}

Startup.Auth.cs

public void ConfigureAuth(IAppBuilder app)
{
    DatabaseContext dbContext = DependencyResolver.Current.GetService<DatabaseContext>();

    app.CreatePerOwinContext<IUserStore<DatabaseContext>>(() => ApplicationUserStore.Create(dbContext));

    app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);

}

Global.asax.cs

var container = new SimpleInjector.Container();
container.Options.DefaultScopedLifestyle = new WebRequestLifestyle();
container.Register<DatabaseContext>(Lifestyle.Scoped);
container.Verify();

Many Thanks, Tom

As requested here is the stack trace

System.AggregateException
HResult=0x80131500
Message=One or more errors occurred.
Source=mscorlib
StackTrace:
 at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
 at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
 at System.Threading.Tasks.Task`1.get_Result()
 at Controllers.LoginController.Login(LoginModel model) in C:\...\Controllers\LoginController.cs:line 205
 at Controllers.LoginController.Index(LoginModel model) in C:\...\Controllers\LoginController.cs:line 111
 at System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters)
 at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters)
 at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
 at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c.<BeginInvokeSynchronousActionMethod>b__9_0(IAsyncResult asyncResult, ActionInvocation innerInvokeState)
 at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`2.CallEndDelegate(IAsyncResult asyncResult)
 at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.End()
 at System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult)
 at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.<>c__DisplayClass11_0.<InvokeActionMethodFilterAsynchronouslyRecursive>b__0()
 at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.<>c__DisplayClass11_2.<InvokeActionMethodFilterAsynchronouslyRecursive>b__2()

Inner Exception 1:
NotSupportedException: A second operation started on this context before a previous asynchronous operation completed. Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe.

Solution

  • The problem is that a second user ( which is running in different thread ) is trying to access the DatabaseContext while it is used by first user. You should create a new DatabaseContext for every thread