c# - HttpContext.Current 在异步回调中为 null

标签 c# asp.net .net wcf asynchronous

尝试在方法回调中访问 HttpContext.Current 以便修改 Session 变量,但是我收到异常 HttpContext.Currentnull。当 _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() 获得相同的 SessionIDSession 变量 方法。

我应该启用 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/

相关文章:

c# - 左移整个字节数组

c# - 如何在 ASP.Net Core MVC 应用程序中获取 Action 和 Controller 名称?

c# - 自定义列表框设计

asp.net - Javascript结果生成Jquery CRUD ajax操作

.net - 为什么 ICollection<T> 不实现 ICollection?

c# - 你能在编译过程中只包含某些形式吗

c# - 在两个表单之间传输数据而无需再次创建新实例

c# - 外键可能导致循环或多个级联路径

c# - Visual Studio 2017 无法在 Windows 10.0 上启动 IIS Express 10.0

.NET 元组和等于性能