J'essaie d'accéder à _HttpContext.Current
_ dans un rappel de méthode, donc je peux modifier une variable Session
, mais je reçois l'exception que _HttpContext.Current
_ est null
. La méthode de rappel est déclenchée de manière asynchrone, lorsque l'objet __anAgent
_ le déclenche.
Je ne suis toujours pas sûr de la solution à cela après avoir vu similairequestions sur SO.
Une version simplifiée de mon code ressemble à ceci:
_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"]
}
}
_
Je ne sais pas si nécessaire, mais ma classe WorkAgent
ressemble à ceci:
_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);
}
}
_
Que se passe-t-il dans le code:
Cliquer sur un bouton appelle la méthode SendData()
, à l'intérieur de celle-ci __webAgent
_ envoie un message à un autre serveur et attend la réponse (en attendant, l'utilisateur peut toujours interagir avec cette page et se référer à la même SessionID
). Une fois reçu, il appelle la méthode BackendReceived()
qui, de retour dans la page .aspx.cs, appelle la méthode Callback()
.
Question:
Lorsque WorkAgent
déclenche la méthode Callback()
, il essaie d'accéder à _HttpContext.Current
_ qui est null
. Pourquoi est-ce le cas lorsque, si je continue, en ignorant l'exception, je peux toujours obtenir les mêmes variables SessionID
et Session
en utilisant la méthode ajax retournée GetSessionVar()
.
Dois-je activer le paramètre aspNetCompatibilityEnabled ?
Dois-je créer une sorte de gestionnaire de module asynchrone ?
Est-ce lié à mode intégré/classique ?
Voici une solution basée sur une classe qui fonctionne jusqu'à présent dans des cas simples dans MVC5 (MVC6 prend en charge un contexte basé sur 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;
}
}
}
Pour l'utiliser, vous pouvez vous connecter à partir de 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();
}
Vous pouvez ensuite l'utiliser à la place de HttpContext.Current:
HttpContextProvider.Current
Il peut y avoir des problèmes car je ne comprends pas cela actuellement réponse connexe . Commentez s'il vous plaît.
Référence: AsyncLocal (nécessite .NET 4.6)
Veuillez consulter l'article suivant pour une explication sur la raison pour laquelle la variable de session est nulle et les solutions possibles
http://adventuresdotnet.blogspot.com/2010/10/httpcontextcurrent-and-threads-with.html
cité à partir de l'article;
le
HttpContext
actuel est en fait dans le stockage local des threads, ce qui explique pourquoi les threads enfants n'y ont pas accès
Et comme le dit un travail proposé autour de l'auteur
passez-y une référence dans votre thread enfant. Incluez une référence à
HttpContext
dans l'objet "state" de votre méthode de rappel, puis vous pouvez la stocker dansHttpContext.Current
sur ce fil
Lorsque vous utilisez des threads ou une fonction async
, HttpContext.Current
n'est pas disponible.
Essayez d'utiliser:
HttpContext current;
if(HttpContext != null && HttpContext.Current != null)
{
current = HttpContext.Current;
}
else
{
current = this.CurrentContext;
//**OR** current = threadInstance.CurrentContext;
}
Une fois que vous avez défini current
avec une instance appropriée, le reste de votre code est indépendant, qu'il soit appelé à partir d'un thread ou directement à partir d'un WebRequest
.