HttpContext.Current在异步Callback中为null

尝试在方法回调中访问HttpContext.Current ,所以我可以修改Session变量,但是我收到HttpContext.Currentnull的exception。 当_anAgent对象触发它时,异步触发回调方法。

在查看SO上的类似 问题之后,我仍然不确定解决方案。

我的代码的简化版本如下所示:

 public partial class Index : System.Web.UI.Page protected void Page_Load() { // aCallback is an Action, triggered when a callback is received _anAgent = new WorkAgent(..., aCallback: Callback); ... HttpContext.Current.Session["str_var"] = _someStrVariable; } protected void SendData() // Called on button click { ... var some_str_variable = HttpContext.Current.Session["str_var"]; // The agent sends a message to another server and waits for a call back // which triggers a method, asynchronously. _anAgent.DispatchMessage(some_str_variable, some_string_event) } // This method is triggered by the _webAgent protected void Callback(string aStr) { // ** This culprit throws the null exception ** HttpContext.Current.Session["str_var"] = aStr; } [WebMethod(EnableSession = true)] public static string GetSessionVar() { return HttpContext.Current.Session["str_var"] } } 

不确定是否有必要,但我的WorkAgent类看起来像这样:

 public class WorkAgent { public Action OnCallbackReceived { get; private set; } public WorkAgent(..., Action aCallback = null) { ... OnCallbackReceived = aCallback; } ... // This method is triggered when a response is received from another server public BackendReceived(...) { ... OnCallbackReceived(some_string); } } 

代码中会发生什么:
单击按钮调用SendData()方法,在此内部_webAgent将消息调度到另一个服务器并等待回复(同时用户仍然可以与此页面交互并引用相同的SessionID )。 收到后,它调用BackendReceived()方法,该方法在.aspx.cs页面中调用Callback()方法。

题:
WorkAgent触发Callback()方法时,它尝试访问null HttpContext.Current 。 为什么如果我继续,忽略exception,我仍然可以使用ajax返回的GetSessionVar()方法获取相同的SessionIDSession变量。

我应该启用aspNetCompatibilityEnabled设置吗?
我应该创建某种异步模块处理程序吗?
这与集成/经典模式有关吗?

这是一个基于类的解决方案,适用于MVC5中迄今为止的简单案例(MVC6支持基于DI的上下文)。

 using System.Threading; using System.Web; namespace SomeNamespace.Server.ServerCommon.Utility { ///  /// Preserve HttpContext.Current across async/await calls. /// Usage: Set it at beginning of request and clear at end of request. ///  static public class HttpContextProvider { ///  /// Property to help ensure a non-null HttpContext.Current. /// Accessing the property will also set the original HttpContext.Current if it was null. ///  static public HttpContext Current => HttpContext.Current ?? (HttpContext.Current = __httpContextAsyncLocal?.Value); ///  /// MVC5 does not preserve HttpContext across async/await calls. This can be used as a fallback when it is null. /// It is initialzed/cleared within BeginRequest()/EndRequest() /// MVC6 may have resolved this issue since constructor DI can pass in an HttpContextAccessor. ///  static private AsyncLocal __httpContextAsyncLocal = new AsyncLocal(); ///  /// Make the current HttpContext.Current available across async/await boundaries. ///  static public void OnBeginRequest() { __httpContextAsyncLocal.Value = HttpContext.Current; } ///  /// Stops referencing the current httpcontext ///  static public void OnEndRequest() { __httpContextAsyncLocal.Value = null; } } } 

要使用它可以从Global.asax.cs挂钩:

  public MvcApplication() // constructor { PreRequestHandlerExecute += new EventHandler(OnPreRequestHandlerExecute); EndRequest += new EventHandler(OnEndRequest); } protected void OnPreRequestHandlerExecute(object sender, EventArgs e) { HttpContextProvider.OnBeginRequest(); // preserves HttpContext.Current for use across async/await boundaries. } protected void OnEndRequest(object sender, EventArgs e) { HttpContextProvider.OnEndRequest(); } 

然后可以使用它来代替HttpContext.Current:

  HttpContextProvider.Current 

可能存在问题,因为我目前不理解这个相关的答案 。 请评论。

参考: AsyncLocal (需要.NET 4.6)

使用线程或async函数时, HttpContext.Current不可用。

尝试使用:

 HttpContext current; if(HttpContext != null && HttpContext.Current != null) { current = HttpContext.Current; } else { current = this.CurrentContext; //**OR** current = threadInstance.CurrentContext; } 

一旦使用适当的实例设置current ,代码的其余部分是独立的,无论是从线程调用还是直接从WebRequest调用。

有关Session变量为空的原因以及可能的解决方法,请参阅以下文章

http://adventuresdotnet.blogspot.com/2010/10/httpcontextcurrent-and-threads-with.html

引自文章;

当前的HttpContext实际上是在线程本地存储中,这解释了为什么子线程无法访问它

正如围绕着作者的拟议工作所说的那样

在您的子线程中传递对它的引用。 在回调方法的“state”对象中包含对HttpContext的引用,然后可以将它存储到该线程上的HttpContext.Current