Sto utilizzando ASP.NET MVC 5 e Identity Framework. Quando chiamo UserManager.UpdateAsync (...) verranno eseguiti i miei eventhandlers su ApplicationDbContext() SaveChanges. Qui sto usando HttpContext.Current per diversi scopi (logging e auditing) quindi devo dire all'utente corrente. Tuttavia, l'intero metodo viene eseguito in un thread di lavoro e HttpContext.Current è null.HttpContext.Current è nullo nei metodi di Identity Framework
Il problema più grande che i metodi di "sincronizzazione" di UserManager sono solo wrapper attorno alla versione asincrona, quindi le chiamate sono serializzate, ma i metodi (e gli eventhandler) continuano a essere eseguiti in un thread di lavoro diverso.
Si prega di notare che questo problema non ha nulla a che fare con il contesto async/await. Nel controller dopo l'attesa (o chiamando la versione 'sync') ho indietro il HttpContext corretto, anche il metodo del controller sta continuando in un altro thread. Va bene.
Quindi il problema è all'interno del lavoratore asincrono che verrà eseguito in entrambe le versioni "sync" e async. Penso di capire i fenomeni (ma non sono contento delle false versioni del metodo "sync", i veri metodi di sincronizzazione non mostrerebbero questo problema). Solo che non so come gestirlo/risolverlo.
[btw: Non sarebbe più naturale implementare le operar di UserManager come semplici versioni di pura sincronizzazione, quindi avvolgerle in wrapper multithreading asincroni ?. Se continuiamo questa moda asincrona senza pensare, inventeremo presto l'operatore di assegnazione asincrona. Mi costa dozzine di ore (solo questo numero) e costa miliardi di dollari in tutto il mondo, sono sicuro in molti casi meno ritorno del suo prezzo.]
Bonus: Stiamo parlando di UserManager che ha un impatto piuttosto marginale, ma lo stesso principi e problemi possono applicare qualsiasi libreria fuori dalla scatola (scatola nera per te) che gli autori non implementano le versioni di sincronizzazione e o non si preoccupano del contesto del thread del controller. Che dire di EF, non è così marginale ... e che dire dell'infrastruttura di istanziazione di contenitori DI come "ambito di richiesta" o "ambito di sessione". Sicuramente si comportano male se la risoluzione si verifica in una discussione senza HttpContext.Current. Recentemente ho aggiornato SendGrid NuGet e (come una modifica sostanziale) il metodo Deliver() è andato, e ora solo DeliverAsync() esiste ...
Mi piacerebbe avere un modo sicuro affidabile, come posso accedere a HttpContext all'interno di questo lavoratore per scopi di registrazione e controllo.
codice di esempio, il controller versione 'sync':
[AcceptVerbs(HttpVerbs.Post)]
public virtual ActionResult Edit(ApplicationUser user)
{
// validation etc
// Update() seems to be only a poor wrapper around the async version, still uses a worker thread.
var result = UserManager.Update(user);
// Note: HttpContext is correct here so it is not an async/await problem
// error handling, creating ActionResult etc.
}
codice di esempio, la versione del controller asincrono:
[AcceptVerbs(HttpVerbs.Post)]
public virtual async Task<ActionResult> Edit(ApplicationUser user)
{
// validation etc
var result = await UserManager.UpdateAsync(user);
// Note: HttpContext is correct here so it is not an async/await problem
// error handling, creating ActionResult etc.
}
e il gestore di eventi in cui HttpContext è nullo:
public ApplicationDbContext() : base("DefaultConnection", false)
{
InitializeAudit();
}
private void InitializeAudit()
{
var octx = ((IObjectContextAdapter) this).ObjectContext;
octx.SavingChanges +=
(sender, args) =>
{
// HttpContext.Current is null here
};
}
Qualche idea?
Considerare la visualizzazione del codice ... –
Puoi mostrare il codice specifico che stai chiedendo? – i3arnon
Che ne dici di memorizzare 'HttpContext.Current' in una variabile locale prima di creare qualsiasi attività e passarla in qualche modo a task/threads. – EZI