在 ASP.NET Identity GetRolesAsync 中使用异步等待丢失 HttpContext

Losing HttpContext with async await in ASP.NET Identity GetRolesAsync

与 ASP.NET 身份相比,这更像是一个 async/await 问题。我正在使用 Asp.Net Identity,并且有一个自定义的 UserStore,以及一个自定义的 GetRolesAsync 方法。 UserManager 是从 WebApi 控制器调用的。

public class MyWebApiController {
    private MyUserManager manager = new MyUserManager(new MyUserStore());
    [HttpGet]
    public async Task<bool> MyWebApiMethod(int x) {
        IList<string> roles = await manager.GetRolesAsync(x);
        return true;
    }
}
public class MyUserManager : UserManager<MyUser, int> {

    // I do not implement a custom GetRolesAsync in the UserManager, but 
    // from looking at the identity source, this is what the base class is doing:

    // public virtual async Task<IList<string>> GetRolesAsync(TKey userId)
    // {
    //     ThrowIfDisposed();
    //     var userRoleStore = GetUserRoleStore();
    //     var user = await FindByIdAsync(userId).WithCurrentCulture();
    //     if (user == null)
    //     {
    //         throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, Resources.UserIdNotFound,userId));
    //     }
    //     return await userRoleStore.GetRolesAsync(user).WithCurrentCulture();
    // }
}

public class MyUserStore {
    public async Task<IList<string>> GetRolesAsync(TUser user) {

        // I need HttpContext here and it is NULL. WHY??
        var currentContext = System.Web.HttpContext.Current; // NULL!

         var query = from userRole in _userRoles
                    where userRole.UserId.Equals(userId)
                    join role in _roleStore.DbEntitySet on userRole.RoleId equals role.Id
                    select role.Name;

        return await query.ToListAsync();
    }
}

为什么 MyUserStore.GetRolesAsync 中的上下文为空?我以为 await 传递了上下文?我在 MyUserStore 中遍历了其他异步方法,它们都有正确的上下文,而且代码看起来几乎相同。

结果是 TaskExtensions.WithCurrentCulture 的 属性。这些是 EF 的 docs,但它们也适用于 ASP.NET 身份:

Configures an awaiter used to await this Task to avoid marshalling the continuation back to the original context, but preserve the current culture and UI culture.

这会导致同步上下文不编组,从而导致 HttpContext 变为 null

这里是the source的相关部分:

public void UnsafeOnCompleted(Action continuation)
{
    var currentCulture = Thread.CurrentThread.CurrentCulture;
    var currentUiCulture = Thread.CurrentThread.CurrentUICulture;

    // This part is critical.
    _task.ConfigureAwait(false).GetAwaiter().UnsafeOnCompleted(() =>
    {
        var originalCulture = Thread.CurrentThread.CurrentCulture;
        var originalUiCulture = Thread.CurrentThread.CurrentUICulture;
        Thread.CurrentThread.CurrentCulture = currentCulture;
        Thread.CurrentThread.CurrentUICulture = currentUiCulture;
        try
        {
            continuation();
        }
        finally
        {
            Thread.CurrentThread.CurrentCulture = originalCulture;
            Thread.CurrentThread.CurrentUICulture = originalUiCulture;
        }
    });
}