在Identity Framework的方法中,HttpContext.Current为null

g.p*_*dou 9 c# asp.net-mvc async-await asp.net-identity asp.net-identity-2

我正在使用ASP.NET MVC 5和Identity Framework.当我在ApplicationDbContext()上调用UserManager.UpdateAsync(...)我的事件处理程序时,将运行SaveChanges.这里我使用HttpContext.Current用于不同的目的(日志记录和审计)所以我必须说当前用户.但是整个方法在一个工作线程中运行,这里HttpContext.Current为null.

UserManager的"同步"方法最大的问题只是异步版本的包装,因此调用是序列化的,但方法(和事件处理程序)仍然在不同的工作线程中运行.

请注意,此问题与async/await上下文无关.在await之后的控制器中(或调用'sync'版本)我已经返回正确的HttpContext,即使控制器的方法在另一个线程中继续.没关系.

所以问题出在异步工作者中,它将同时在"同步"和异步版本中运行.我想我理解这种现象(但我对假'同步'方法版本不满意,真正的同步方法不会出现这个问题.)我只是不知道如何处理/解决它.

[btw:将UserManager的操作实现为简单的纯同步版本,然后用异步多线程包装器包装它们不是更自然吗?如果我们继续这种异步方式而不考虑我们将很快发明异步赋值运算符.这花了我几十个小时(只是这个问题),而且全世界都要花费数十亿美元,我相信在很多情况下,它的回报要低于它的价格.

额外奖励:我们所说的UserManager的影响非常小,但相同的原则和问题可以应用任何开箱即用的库(黑盒子),作者没有实现同步版本,或者不关心控制器线程的上下文.那么EF,它不是那么边缘......那么DI容器实例化基础设施如"请求范围"或"会话范围"呢?如果在没有HttpContext.Current的线程中进行解析,他们肯定会行为不端.最近我刷新了SendGrid NuGet,并且(作为一个突破性的变化)Deliver()方法消失了,现在只有DeliverAsync()存在...

我想有一个安全可靠的方法,如何访问此worker中的HttpContext以进行日志记录和审计.

示例代码,控制器'sync'版本:

[AcceptVerbs(HttpVerbs.Post)]
public virtual ActionResult Edit(ApplicationUser user)
{
    // validation etc
    // Update() seems to be only a poor wrapper around the async version, still uses a worker thread.
    var result = UserManager.Update(user);
    // Note: HttpContext is correct here so it is not an async/await problem

    // error handling, creating ActionResult etc.
}
Run Code Online (Sandbox Code Playgroud)

示例代码,控制器异步版本:

[AcceptVerbs(HttpVerbs.Post)]
public virtual async Task<ActionResult> Edit(ApplicationUser user)
{
    // validation etc
    var result = await UserManager.UpdateAsync(user);
    // Note: HttpContext is correct here so it is not an async/await problem

    // error handling, creating ActionResult etc.
}
Run Code Online (Sandbox Code Playgroud)

和HttpContext为null的事件处理程序:

public ApplicationDbContext() : base("DefaultConnection", false)
{
    InitializeAudit();
}

private void InitializeAudit()
{
    var octx = ((IObjectContextAdapter) this).ObjectContext;

    octx.SavingChanges +=
        (sender, args) =>
        {
            // HttpContext.Current is null here
        };
}
Run Code Online (Sandbox Code Playgroud)

有任何想法吗?

Row*_*man 4

正如您所说,这是由于线程而发生的。该委托在不同的线程中运行,导致 HttpContext 无法访问。

您可以将变量移到委托之外,使其成为闭包。

private void InitializeAudit()
{
    var octx = ((IObjectContextAdapter) this).ObjectContext;
    HttpContext context = HttpContext.Current;

    octx.SavingChanges +=
        (sender, args) =>
        {
            // context is not null
        };
}
Run Code Online (Sandbox Code Playgroud)