2014-07-03 18 views
19

Cercando di accedere al HttpContext.Current in una chiamata di metodo indietro così posso modificare una variabile Session, tuttavia ricevo l'eccezione che è HttpContext.Currentnull. Il metodo di callback viene attivato in modo asincrono, quando l'oggetto _anAgent lo attiva.HttpContext.Current è nullo in un callback asincrono

Sono ancora incerto sulla soluzione a questo dopo aver visualizzato similarquestions su SO.

Una versione semplificata del mio codice è in questo modo:

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"] 
    } 
} 

Non sono sicuro se necessario, ma la mia classe WorkAgent assomiglia così:

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); 
    } 
} 

Cosa succede nel codice:
Cliccando su un pulsante si chiama il metodo SendData(), all'interno di questo il _webAgent invia un messaggio ad un altro server e attende la risposta (nel frattempo l'utente può comunque interagire con questa pagina e fare riferimento allo stesso SessionID). Una volta ricevuto, chiama il metodo BackendReceived() che, nella pagina .aspx.cs, chiama il metodo Callback().

Domanda:
Quando il WorkAgent innesca il metodo Callback() si tenta di accedere HttpContext.Current che è null. Perché è così quando, se continuo, ignorando l'eccezione, posso ancora ottenere lo stesso SessionID e la variabile Session utilizzando il metodo restituito ajax GetSessionVar().

Devo abilitare l'impostazione aspNetCompatibilityEnabled?
Devo creare una specie di asynchronous module handler?
E 'collegato a Integrated/Classic mode?

+0

perché complicare questo utilizzando callback, una soluzione migliore potrebbe essere quella di utilizzare ajax dal lato client, in questo modo l'utente può comunque interagire con il sito. E la chiamata all'altro sistema può essere semplicemente una normale chiamata di metodo – 3dd

+0

Ajax * è * utilizzato dal lato client per la maggior parte, semplicemente non lo include nel codice sopra (aggiorna le variabili 'HttpContext Session' e il database SQL) . L'unico metodo che non è una chiamata Ajax è 'SendData()'. Questo invia i dati a * qualche altro * server. Sono solo confuso perché il 'HttpContect.Current' diventa ** null ** sul callback. –

+0

Si prega di vedere la mia risposta per una spiegazione sul perché questo sta accadendo – 3dd

risposta

3

Vedere il seguente articolo per una spiegazione sul motivo per cui la variabile di sessione è nullo, e arounds di lavoro possibile

http://adventuresdotnet.blogspot.com/2010/10/httpcontextcurrent-and-threads-with.html

citato dalla dall'articolo;

la corrente HttpContext è in realtà nella memoria thread-local, il che spiega il motivo per cui le discussioni bambini non hanno accesso ad esso

E come una proposta di lavoro intorno l'autore dice

passare un riferimento ad esso nel tuo thread figlio. Includere un riferimento al HttpContext nell'oggetto “stato” del metodo di callback, e quindi è possibile memorizzare per HttpContext.Current su quel thread

3

Quando si utilizza thread o una funzione async, HttpContext.Current non è disponibile.

Provare a utilizzare:

HttpContext current; 
if(HttpContext != null && HttpContext.Current != null) 
{ 
    current = HttpContext.Current; 
} 
else 
{ 
    current = this.CurrentContext; 
    //**OR** current = threadInstance.CurrentContext; 
} 

Una volta impostato current con una vera e propria istanza, il resto del codice è indipendente, se chiamato da un filo o direttamente da un WebRequest.

6

Ecco una soluzione basata su classi che funziona per casi semplici finora in MVC5 (MVC6 supporta un contesto basato su 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; 
     } 
    } 
} 

di usarlo può agganciare in da 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(); 
    } 

quindi possibile utilizzare questo al posto di HttpContext.Current:

HttpContextProvider.Current 

ci possono essere problemi come Al momento non capisco questo related answer. Per favore, commenta.

Riferimento: AsyncLocal (richiede .NET 4.6)

Problemi correlati