メソッドコールバックでHttpContext.Current
にアクセスしようとすると、Session
変数を変更できますが、HttpContext.Current
がnull
であるという例外を受け取ります。 _anAgent
オブジェクトがそれをトリガーすると、コールバックメソッドは非同期に起動されます。
見た後、これに対する解決策がまだわからない 類似質問 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
)。 .aspx.csページに戻ってBackendReceived()
メソッドを呼び出すCallback()
メソッドを呼び出します。
質問:WorkAgent
がCallback()
メソッドをトリガーすると、null
であるHttpContext.Current
にアクセスしようとします。例外を無視して続行すると、ajaxが返したGetSessionVar()
メソッドを使用して、同じSessionID
とSession
変数を取得できる場合があります。
aspNetCompatibilityEnabled 設定を有効にする必要がありますか?
何らかの種類の 非同期モジュールハンドラー を作成する必要がありますか?
これは Integrated/Classicモード に関連していますか?
これは、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からフックできます。
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が必要)
Session変数がnullである理由、および考えられる回避策の説明については、次の記事を参照してください。
http://adventuresdotnet.blogspot.com/2010/10/httpcontextcurrent-and-threads-with.html
記事から引用。
現在の
HttpContext
は実際にはスレッドローカルストレージにあります。これは、子スレッドがアクセスできない理由を説明しています
そして、著者の周りの提案された仕事が言うように
子スレッドでそれへの参照を渡します。
HttpContext
への参照をコールバックメソッドの「状態」オブジェクトに含めると、そのスレッドのHttpContext.Current
に保存できます
スレッドまたはasync
関数を使用する場合、HttpContext.Current
利用できません。
使用してみてください:
HttpContext current;
if(HttpContext != null && HttpContext.Current != null)
{
current = HttpContext.Current;
}
else
{
current = this.CurrentContext;
//**OR** current = threadInstance.CurrentContext;
}
current
を適切なインスタンスに設定すると、スレッドから呼び出されるか、WebRequest
から直接呼び出されるかにかかわらず、残りのコードは独立しています。