HttpContext.Current在异步Callback中为null
尝试在方法回调中访问HttpContext.Current
,所以我可以修改Session
变量,但是我收到HttpContext.Current
为null
的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()
方法获取相同的SessionID
和Session
变量。
我应该启用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
中