0

I have the following methods in the Global.asax of my MVC app:

protected void Application_OnPostAuthenticateRequest(object sender, EventArgs e)
{
    SetUserRolesToSession().GetAwaiter().GetResult();
}

protected async Task SetUserRolesToSession()
{
    var networkUserIdParts = networkUserId.Split(new char[] { '\\' });
    var baseUrl = ConfigurationManager.AppSettings["SecurityApiBaseUrl"].ToString();
    var userSearchRequest = new UserSearchRequest
    {
        NetworkUserId = Request.LogonUserIdentity.Name
    };
    var user = (await new SecurityApiServiceWrapper(baseUrl)
        .GetUsersAsync(userSearchRequest)).FirstOrDefault();

    Session["Roles" + "_" + networkUserId] = user?.Groups.Select(x => x.Name).ToList();
}

Then I have the following code in SecurityApiServiceWrapper:

public async Task<List<User>> GetUsersAsync(UserSearchRequest userSearchRequest)
{
    using (var client = GetHttpClient(_baseUrl))
    {
        var response = await client.PostAsJsonAsync("api/v1/users/search", userSearchRequest);
        if (response.IsSuccessStatusCode)
        {
            var users = await response.Content.ReadAsAsync<List<User>>();
            return users;
        }
        else //not 200
        {
        }
    }
}

public HttpClient GetHttpClient(string baseUrl)
{
    var client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true });
    client.BaseAddress = new Uri(baseUrl);
    client.Timeout = Timeout.InfiniteTimeSpan;
    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Accept.Add(
        new MediaTypeWithQualityHeaderValue("application/json"));
    return client;
}

When the code steps into await client.PostAsJsonAsync, the process gets stuck and never returns. I can make the same api call through Postman and the response returns in < 1s. Any idea what the issue might be or how to debug?

user9393635
  • 1,369
  • 4
  • 13
  • 32
  • `SetUserRolesToSession().GetAwaiter().GetResult()` probably causing a deadlock. Basically, your `PostAsJsonAsync` continuation will be waiting for a time at which the current context is available, but it'll never be available because of `GetResult()` being a blocking call. Should be a simple fix of changing `Application_OnPostAuthenticateRequest` to `async void` and then call `await SetUserRolesToSession();` instead. – Glorin Oakenfoot Jun 01 '18 at 17:01
  • @GlorinOakenfoot - changing to "async void" returns the following error:"An asynchronous operation cannot be started at this time. Asynchronous operations may only be started within an asynchronous handler or module or during certain events in the Page lifecycle. If this exception occurred while executing a Page, ensure that the Page is marked <%@ Page Async="true" %>. This exception may also indicate an attempt to call an "async void" method, which is generally unsupported within ASP.NET request processing. Instead, the asynchronous method should return a Task, and the caller should await it" – user9393635 Jun 01 '18 at 17:09
  • @GlorinOakenfoot - if I modify the signature of "Application_OnPostAuthenticateRequest" with "async Task" then MVC does not recognize it as an event handler and it does not get hit – user9393635 Jun 01 '18 at 17:13
  • 2
    It looks as if the answer may be to use `EventHandlerTaskAsyncHelper` and `AddOnPostAuthenticateRequestAsync()`. See [this answer](https://stackoverflow.com/a/22424797/4372746) for more info. It's a little old but it does not seem as though a better answer is out there. – Glorin Oakenfoot Jun 01 '18 at 17:21
  • @glorin thanks for that url. I'll take a look. can you please provide more explanation about the root cause you described in your first post or provide a good url that would give me some additional insight about this behavior. basically, I removed GetResult() from my code and the code didn't lock up. I didn't get a completed response of course but I'd like to get a better understanding of the details of GetAwaiter().GetResult() b/c I know it works well for me in some scenarios but I don't think I have a very good understanding of why it doesn't work for me in some scenarios – user9393635 Jun 01 '18 at 17:26
  • `.GetResult()` can't exit because there is no result, `Task` is async synonym for `void`. For non-generic tasks you need to use the `Wait()` method to wait synchronously til the end of the task. However, the @GlorinOakenfoot' suggestion looks better for me – VMAtm Jun 01 '18 at 18:08
  • @user9393635 - The short answer is... GetResult() blocks on the current thread. When you await something, basically it takes all the code after that point, wraps it up, and saves it to be run when the await is done. This wrapped-up code is what I referred to as the *continuation*. I won't get into specifics, but in many cases the continuation code will be run on the original thread (i.e. the original context). However, it needs the thread to not be busy before it can run, and since GetResult() is busy blocking the thread, it can never complete. Classic wait-for-resource deadlock problem. – Glorin Oakenfoot Jun 01 '18 at 21:20
  • You can reference [this link](https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html) for probably a better explanation. His ASP.NET example is very similar to your situation. The takeway should basically be: `GetResult()`, `.Wait()`, and `.Result` (and probably more) can easily result in this deadlock and should only be used with care. – Glorin Oakenfoot Jun 01 '18 at 21:24

0 Answers0