尝试在方法回调中访问 HttpContext.Current
以便修改 Session
变量,但是我收到异常 HttpContext.Current
为 null
。当 _anAgent
对象触发回调方法时,它会异步触发。
在查看 similar 后,我仍然不确定此问题的解决方案questions在 SO 上。
我的代码的简化版本如下所示:
public partial class Index : System.Web.UI.Page
protected void Page_Load()
{
// aCallback is an Action<string>, 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<string> OnCallbackReceived { get; private set; }
public WorkAgent(...,
Action<string> 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()
方法时,它会尝试访问 HttpContext.Current
,它是 null
。为什么会这样,如果我继续,忽略异常,我仍然可以使用返回的 ajax GetSessionVar() 获得相同的
方法。SessionID
和 Session
变量
我应该启用 aspNetCompatibilityEnabled 吗?设置?
我应该创建某种 asynchronous module handler 吗? ?
这与Integrated/Classic mode有关吗? ?
最佳答案
这是一个基于类的解决方案,目前适用于 MVC5 中的简单案例(MVC6 支持基于 DI 的上下文)。
using System.Threading;
using System.Web;
namespace SomeNamespace.Server.ServerCommon.Utility
{
/// <summary>
/// Preserve HttpContext.Current across async/await calls.
/// Usage: Set it at beginning of request and clear at end of request.
/// </summary>
static public class HttpContextProvider
{
/// <summary>
/// Property to help ensure a non-null HttpContext.Current.
/// Accessing the property will also set the original HttpContext.Current if it was null.
/// </summary>
static public HttpContext Current => HttpContext.Current ?? (HttpContext.Current = __httpContextAsyncLocal?.Value);
/// <summary>
/// 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.
/// </summary>
static private AsyncLocal<HttpContext> __httpContextAsyncLocal = new AsyncLocal<HttpContext>();
/// <summary>
/// Make the current HttpContext.Current available across async/await boundaries.
/// </summary>
static public void OnBeginRequest()
{
__httpContextAsyncLocal.Value = HttpContext.Current;
}
/// <summary>
/// Stops referencing the current httpcontext
/// </summary>
static public void OnEndRequest()
{
__httpContextAsyncLocal.Value = null;
}
}
}
要使用它可以从 Global.asax.cs Hook :
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
可能存在问题,因为我目前不理解 related answer .请评论。
引用:AsyncLocal (需要 .NET 4.6)
关于c# - HttpContext.Current 在异步回调中为 null,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24544272/