32

My problem is very similar to this question here. I have an AuthenticationService class that makes an HttpClient PostAsync() and never returns the result when I am running it from the ASP project, but when I implement it in a Console app it works just fine.

This is my Authentication Service class:

public class AuthenticationService : BaseService
{
    public async Task<Token> Authenticate (User user, string url)
    {
        string json = JsonConvert.SerializeObject(user);
        StringContent content = new StringContent(json, Encoding.UTF8, "application/json");

        HttpResponseMessage response = await _client.PostAsync(url, content);
        string responseContent = await response.Content.ReadAsStringAsync();
        Token token = JsonConvert.DeserializeObject<Token>(responseContent);

        return token;
    }
}

And it is here where it hangs: HttpResponseMessage response = await _client.PostAsync(url, content);

Here is my Controller calling the service:

public ActionResult Signin(User user)
{
    // no token needed to be send - we are requesting one
    Token token =  _authenticationService.Authenticate(user, ApiUrls.Signin).Result;
    return View();
}

Here is an example of how I have been testing the service using a Console app, and it runs just fine.

class Program
{
    static void Main()
    {
        AuthenticationService auth = new AuthenticationService();

        User u = new User()
        {
            email = "email@hotmail.com",
            password = "password123"
        };

        Token newToken = auth.Authenticate(u, ApiUrls.Signin).Result;

        Console.Write("Content: " + newToken.user._id);
        Console.Read();
    }
}
CarenRose
  • 1,266
  • 1
  • 12
  • 24
Erik
  • 947
  • 2
  • 9
  • 20

3 Answers3

37

Since you are using .Result, this will end up causing a deadlock in your code. The reason this is working in a console application is because console applications don't have contexts, but ASP.NET apps do (see Stephen Cleary's Don't Block on Async Code). You should make the Signin method in your controller async and await the call to _authenticationService.Authenticate to resolve the deadlock issue.

CarenRose
  • 1,266
  • 1
  • 12
  • 24
Josh Dammann
  • 386
  • 3
  • 3
23

Since you are using .Result or .Wait or await this will end up causing a deadlock in your code.

you can use ConfigureAwait(false) in async methods for preventing deadlock

like this:

string responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

you can use ConfigureAwait(false) wherever possible for Don't Block Async Code .

Hasan Fathi
  • 5,610
  • 4
  • 42
  • 60
  • ConfigureAwait may prevent deadlocks. However that's not really it's intention and there are side effects (particularly in websites, where you may not have access to information from HttpContext). It would be better to look far more into ConfigureAwait before you use it. – CodePB Feb 20 '19 at 10:26
  • 1
    The very source you quote above also says: "Using ConfigureAwait(false) to avoid deadlocks is a dangerous practice. You would have to use ConfigureAwait(false) for every await in the transitive closure of all methods called by the blocking code, including all third- and second-party code. Using ConfigureAwait(false) to avoid deadlock is at best just a hack)." – CodePB Feb 20 '19 at 10:27
  • https://msdn.microsoft.com/en-us/magazine/jj991977.aspx This article explains well when ConfigureAwait should be used and what to be aware of. – CodePB Feb 20 '19 at 10:46
6

In case someone comes and needs to see code I just change the controller to something like this:

/***
*** Added async and Task<ActionResult>
****/
public async Task<ActionResult> Signin(User user)
{
    //no token needed - we are requesting one
    // added await and remove .Result()
    Token token =  await _authenticationService.Authenticate(user, ApiUrls.Signin);

    return RedirectToAction("Index", "Dashboard", token.user);
}

Thank you all for your quick response!

pjpscriv
  • 866
  • 11
  • 20
Erik
  • 947
  • 2
  • 9
  • 20