Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PasswordSignInAsync hangs in .Core App

I'm building a .NET core web app in F#, and am trying to set up Identity. I have got a basic version of a Register method working, which creates a user in the database and creates the cookie:

[<HttpPost>]
[<AllowAnonymous>]
member this.Register([<FromBody>]model: RegisterViewModel) =
    if not (isNull model.Email) && model.Password = model.ConfirmPassword then
        let user = ApplicationUser(userName = model.Email, email = model.Email, password = model.Password)
        let result = userManager.CreateAsync(user, model.Password) |> Async.AwaitTask |> Async.RunSynchronously
        if result.Succeeded then
            signInManager.SignInAsync(user, isPersistent = false) |> Async.AwaitTask |> Async.RunSynchronously
            true
        else
           false 
    else
        false

However, my Login implementation hangs:

[<HttpPost>]
[<AllowAnonymous>]
member this.Login([<FromBody>]model: LoginViewModel) =
    if not (isNull model.Email && isNull model.Password) then
        let result = signInManager.PasswordSignInAsync(model.Email, model.Password, false, lockoutOnFailure = false) |> Async.AwaitTask |> Async.RunSynchronously
        if result.Succeeded then true else false
    else
        false

This is where the application hangs:

info: Microsoft.EntityFrameworkCore.Storage.IRelationalCommandBuilderFactory[1]
  Executed DbCommand (5ms) [Parameters=[@__normalizedUserName_0='?' (Size = 256)], CommandType='Text', CommandTimeout='30']
  SELECT TOP(1) [u].[Id], [u].[AccessFailedCount], [u].[ConcurrencyStamp], [u].[Email], [u].[EmailConfirmed], [u].[LockoutEnabled], [u].[LockoutEnd], [u].[NormalizedEmail], [u].[NormalizedUserName], [u].[PasswordHash], [u].[PhoneNumber], [u].[PhoneNumberConfirmed], [u].[SecurityStamp], [u].[TwoFactorEnabled], [u].[UserName]
  FROM [AspNetUsers] AS [u]
  WHERE [u].[NormalizedUserName] = @__normalizedUserName_0

Any idea what the problem could be?

like image 831
Dan O'Leary Avatar asked Dec 11 '16 15:12

Dan O'Leary


1 Answers

According to this article, the handlers can be async (in the C# sense) so it makes sense to rewrite your route-handler to be non-blocking, thus removing the Async.RunSynchronously that is likely causing the issue.

We can write the actual logic in an F# async workflow, because that is more idomatic, and then convert it to a Task<_> in order to match the expected C# signature.

[<HttpPost>]
[<AllowAnonymous>]
member this.Login([<FromBody>]model: LoginViewModel) =
  async {
    if not (isNull model.Email && isNull model.Password) 
    then
      let! result = 
        signInManager.PasswordSignInAsync(model.Email, model.Password, false, lockoutOnFailure = false) 
        |> Async.AwaitTask 

      return result.Succeeded
    else
      return false
  }
  |> Async.StartAsTask

Dead-locks can be very hard to hunt down, so when using F# async you should try to have only one call to Async.RunSynchronously in your application, usually in the "main" method.

like image 114
sdgfsdh Avatar answered Oct 02 '22 13:10

sdgfsdh