Losing HttpContext with async await in ASP.NET Identity GetRolesAsync

三世轮回 提交于 2019-12-24 17:53:06

问题


This is more of an async/await question than ASP.NET Identity. I am using Asp.Net Identity, and have a custom UserStore, with a customized GetRolesAsync method. The UserManager is called from a WebApi controller.

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();
    }
}

Why is context null in MyUserStore.GetRolesAsync? I thought await passed the context down? I've stepped through other async methods in MyUserStore and they all have the correct context, and the code seems virtually identical.


回答1:


This turns out to be a property of TaskExtensions.WithCurrentCulture. These are the docs for EF, but they apply for ASP.NET Identity as well:

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.

This causes the synchronization context not to marshal, which causes HttpContext to be null.

Here is the relevant part from 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;
        }
    });
}


来源:https://stackoverflow.com/questions/30874783/losing-httpcontext-with-async-await-in-asp-net-identity-getrolesasync

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!