【问题标题】:HttpContext.Current is null inside Identity Framework's methodsHttpContext.Current 在 Identity Framework 的方法中为 null
【发布时间】:2015-07-18 20:35:28
【问题描述】:

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

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

请注意,此问题与 async/await 上下文无关。在等待(或调用“同步”版本)之后的控制器中,我返回了正确的 HttpContext,即使控制器的方法也在另一个线程中继续。没关系。

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

[顺便说一句:将 UserManager 的操作实现为简单的纯同步版本,然后用异步多线程包装器包装它们不是更自然吗?如果我们不假思索地继续这种异步方式,我们很快就会发明异步赋值运算符。它花费了我几十个小时(仅这一期),并且在全球范围内花费了数十亿美元,我相信在很多情况下回报低于它的价格。]

奖励:我们谈论的是 UserManager,它的影响非常小,但是相同的原则和问题可以应用任何开箱即用的库(对您来说是黑盒),作者不实现同步版本或不关心控制器线程的上下文。 EF 呢,它不是那么微不足道……还有 DI 容器实例化基础设施,例如“请求范围”或“会话范围”。如果解析发生在没有 HttpContext.Current 的线程中,它们肯定会行为不端。最近我刷新了 SendGrid NuGet,并且(作为一项重大更改)Deliver() 方法消失了,现在只存在 DeliverAsync()...

我想有一个安全可靠的方法,我怎样才能访问这个工作人员内部的 HttpContext 以进行日志记录和审计。

示例代码,控制器“同步”版本:

[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.
}

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

[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.
}

以及 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
        };
}

有什么想法吗?

【问题讨论】:

  • 考虑显示代码...
  • 你能显示你问的具体代码吗?
  • 在创建任何任务并以某种方式将其传递给任务/线程之前将HttpContext.Current 存储在局部变量中怎么样。
  • Alexei:我认为这与 ConfigureAwait(false) 无关,因为 a) 这与等待后丢失上下文无关,(b) 我没有使用它,我也没有看到它的痕迹在反编译的身份框架代码中)。
  • 是的,我正在使用 Owin

标签: c# asp.net-mvc async-await asp.net-identity asp.net-identity-2


【解决方案1】:

正如你所说,这是因为线程。委托在不同的线程中运行,使 HttpContext 无法访问。

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

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

    octx.SavingChanges +=
        (sender, args) =>
        {
            // context is not null
        };
}

【讨论】:

  • 谢谢,我想到了类似的东西。但是仍然不是我创建 ApplicationDbContext 实例,它也是身份框架,因此不能保证在控制器的线程中创建它,因此在它的构造函数中我们没有必要拥有 HttpContext。
【解决方案2】:

您正在通过 owin 使用 asp.net 身份, 因此每个请求都会创建一个 dbcontext 实例, 您可以从请求管道中的任何位置获取此引用。

注意。这很方便,但我认为不应该在管理器之外访问 dbcontext。 在 asp.net 身份设计中,只有经理应该知道商店。 我相信 dbcontext 被暴露是因为几个 asp.net 身份中间件都依赖它。

但是,它可以帮助您解决问题:

允许在类之外设置您的自定义 dbcontext 处理程序:

public EventHandler SavingChangesEventHandler
        {
            set
            {
                (((System.Data.Entity.Infrastructure.IObjectContextAdapter)this).ObjectContext).SavingChanges += value;
            } 
        }

声明一个自定义的 ActionFilter 类并注册它,然后重写 OnActionExecuting:

ASP.NET MVC 中的过滤 https://msdn.microsoft.com/en-us/library/gg416513(VS.98).aspx

public class CustomizeAppDbcontextFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        var dbcontext = HttpContext.GetOwinContext().Get<ApplicationDbContext>();    
        var currentuser = HttpContext.Current.User;

        dbcontext.SavingChangesEventHandler = (sender, args) =>
            {
                // use currentuser
            };  
    }    
}

您可能需要这些 using 语句才能调用 identity.owin 扩展方法:

使用 Microsoft.AspNet.Identity;

使用 Microsoft.AspNet.Identity.Owin;

您应该在控制器线程中,因为 OnActionExecuting 正在包装控制器操作。

我没有测试它,所以它可能需要一些改进,但这个概念应该可以工作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-14
    • 1970-01-01
    • 2013-10-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多