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



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

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

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

所以问题在async worker内部,它将在"sync"和async版本中运行。我想我正在理解这种现象(但我对假的"同步"方法版本不满意,真正的同步方法不会出现这个问题。)我只是不知道如何处理/解决它。

[btw:将UserManager的操作实现为简单的纯同步版本,然后用异步多线程包装器包装它们不是更自然吗?]如果我们不假思索地继续这种异步方式,我们很快就会发明异步赋值操作符。它花了我几十个小时(就这个问题),在世界范围内花费了无数美元,我确信在很多情况下回报比它的价格要少。)

好处:我们正在讨论的UserManager的影响相当小,但同样的原则和问题可以适用于任何开箱即用的库(黑盒),作者不实现同步版本,或者不关心控制器线程的上下文。那EF呢,它不是那么边缘…以及DI容器实例化基础设施,如"请求范围"或"会话范围"。如果解析发生在没有HttpContext.Current的线程中,它们肯定会行为不当。最近我刷新了SendGrid NuGet,并且(作为一个突破性的改变)Deliver()方法消失了,现在只有DeliverAsync()存在…

我想有一个安全可靠的方式,我如何才能访问这个工作者内部的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.
}
示例代码,控制器async版本:
[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为空的事件处理程序:

public ApplicationDbContext() : base("DefaultConnection", false)
{
    InitializeAudit();
}
private void InitializeAudit()
{
    var octx = ((IObjectContextAdapter) this).ObjectContext;
    octx.SavingChanges +=
        (sender, args) =>
        {
            // HttpContext.Current is null here
        };
}

任何想法?

正如您所说,这是由于线程导致的。委托在另一个线程中运行,使得HttpContext不可访问。

你可以将变量移出委托,使其成为闭包。

private void InitializeAudit()
{
    var octx = ((IObjectContextAdapter) this).ObjectContext;
    HttpContext context = HttpContext.Current;
    octx.SavingChanges +=
        (sender, args) =>
        {
            // context is not null
        };
}

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

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

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

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

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

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

ASP中的过滤。NET MVChttps://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语句来调用标识符。自己的扩展方法:

使用Microsoft.AspNet.Identity;

使用Microsoft.AspNet.Identity.Owin;

你应该在控制器线程中,因为onactionexecution正在包装控制器动作。

我没有测试它,所以它可能需要一些润色,但这个概念应该是可行的。

相关内容

  • 没有找到相关文章

最新更新