2

HttpContext.Current null in async after await calls.

Here is my code:

if (!string.IsNullOrEmpty(securityGroupName))
{
    // To remove the domain name from the security group name.
    string securityGroupDisplayName = securityGroupName.Split('\\')[1];
    string serviceSecurityGroupId = await this.graphApiClient.GetGroupIdAsync(securityGroupDisplayName).ConfigureAwait(false);

    if (!string.IsNullOrEmpty(serviceSecurityGroupId))
    {
        Task securityGroupRoleAddTask = this.CheckMembershipAndAddRole(serviceSecurityGroupId, userId, securityGroupName);
        Task flightAdminRoleAddTask = this.CheckMembershipAndAddRole(FlightAdminSecurityGroupId, userId, FlightAdminRoleName);
        Task.WaitAll(securityGroupRoleAddTask, flightAdminRoleAddTask);
    }
    else
    {
        LoggingUtilities.Logger.TraceInformation("Azure AD id does not exist for the security group: {0}.", securityGroupName);
        await this.CheckMembershipAndAddRole(FlightAdminSecurityGroupId, userId, FlightAdminRoleName).ConfigureAwait(false);
    }
}
else
{
    LoggingUtilities.Logger.TraceInformation("Security group name is not valid, checking for flight admin role for the user: {0}.", userAlias);
    await this.CheckMembershipAndAddRole(FlightAdminSecurityGroupId, userId, FlightAdminRoleName).ConfigureAwait(false);
}

// Add the flight privileged users role to be able to verify the user is authorized for the role.
string flightPrivilegedUsersRoleName = RoleRepository.Instance.GetByName(Constants.FlightPrivilegedUsersRoleKey).Name;
if (!this.roles.Contains(flightPrivilegedUsersRoleName, StringComparer.OrdinalIgnoreCase))
{
    LoggingUtilities.Logger.TraceInformation("Adding flight privileged users to roles list for the user: {0}.", userAlias);
    this.roles.Add(flightPrivilegedUsersRoleName);
}

if (userAlias != null)
{
    LoggingUtilities.Logger.TraceInformation("Check security group memberships and assign roles for the user: {0}.", userAlias);
    var newPrincipal = new GenericPrincipal(new GenericIdentity(userAlias), this.roles.ToArray());
    Thread.CurrentPrincipal = newPrincipal;
    HttpContext.Current.User = newPrincipal;
}

The recommendations about the following entries in the web.config entries did not help:

<system.web>
    <compilation debug="false" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" enableVersionHeader="false" requestPathInvalidCharacters="&lt;,&gt;,%,&amp;,\,?" />
</system.web>
<appSettings>
    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
</appSettings>

Any recommendations on how to fix this would be appreciated.

svick
  • 236,525
  • 50
  • 385
  • 514
SteelBird82
  • 759
  • 3
  • 10
  • 23

1 Answers1

7

Remove your .ConfigureAwait(false) calls. They are what causing you trouble. When you call .ConfigureAwait(false), it tells C# that you don't care which thread to use for the completion of async call. You end up in another thread that has doesn't have HttpContext.Current context, since it's a thread pool thread, not ASP.NET thread.

torvin
  • 6,515
  • 1
  • 37
  • 52
  • removing the .ConfigureAwait(false) blocks the execution on the Task.WaitAll(securityGroupRoleAddTask, flightAdminRoleAddTask); statement. What is the right way of solving this without deadlocking. – SteelBird82 Sep 23 '15 at 05:15
  • 1
    You should use `await Task.WhenAll()` instead. In general, you should not mix async and blocking calls. – torvin Sep 23 '15 at 05:16
  • removing the .ConfigureAwait(false) from the entire class and adding await Task.WhenAll() in place of Task.WaitAll throws the exception: System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) – SteelBird82 Sep 23 '15 at 05:36
  • Post the whole exception text – torvin Sep 23 '15 at 05:38
  • There .ConfigureAwait(false) in the calling class that was causing the exception, removing them solved the problem. – SteelBird82 Sep 23 '15 at 05:49
  • Is this the right way of fixing the issue. Is this not recommended to use .ConfigureAwait(false) in the ASP.NET or WebAPI2 context? Is there any other of fixing this without removing the .ConfigureAwait(false). The reason I ask this is as per this SO answer http://stackoverflow.com/questions/24326043/deadlock-reading-async-response-content-from-async-delegatinghandler-in-webapi it is recommended to use .ConfigureAwait(false) on the await calls in the WebApi? – SteelBird82 Sep 23 '15 at 05:53
  • Thanks for the reference! – SteelBird82 Sep 23 '15 at 06:10
  • You should use `ConfigureWait(false)` if you have more than one `await`s. but if you use it on the action method, you'll get the problems you encountered. To avoid all that, create an `async` method that does all the work and use `ConfigureWait(false)` on all `await`s of that method. – Paulo Morgado Sep 23 '15 at 08:41
  • 1
    That's not true about two awaits. And `ConfigureWait(false)` will always cause you loosing the context in ASP.NET. It should not be used. – torvin Sep 23 '15 at 11:55