Singleton Per Call Context (Web Request) in Unity

Qualche giorno fa ho avuto questo problema con il threading ASP.Net. Volevo avere un object singleton per richiesta web. In realtà ho bisogno di questo per la mia unità di lavoro. Volevo istanziare un’unità di lavoro per ogni richiesta web in modo che la mappa delle id quadro fosse valida attraverso la richiesta. In questo modo ho potuto utilizzare un IoC per iniettare il mio IUnitOfWork nelle mie classi di repository in modo trasparente e utilizzare la stessa istanza per interrogare e aggiornare le mie quadro.

Dal momento che sto usando Unity, ho erroneamente utilizzato PerThreadLifeTimeManager. Mi sono presto reso conto che il modello di threading ASP.Net non supporta ciò che voglio ottenere. Fondamentalmente usa un theadpool e ricicla i thread, e ciò significa che ottengo un UnitOfWork per thread !! Tuttavia, quello che volevo era una unità di lavoro per richiesta web.

Un po ‘di googling mi ha dato questo ottimo post . Era esattamente quello che volevo; tranne per la parte di unità che era abbastanza facile da acheive.

Questa è la mia implementazione per PerCallContextLifeTimeManager per unity:

public class PerCallContextLifeTimeManager : LifetimeManager { private const string Key = "SingletonPerCallContext"; public override object GetValue() { return CallContext.GetData(Key); } public override void SetValue(object newValue) { CallContext.SetData(Key, newValue); } public override void RemoveValue() { } } 

E ovviamente lo uso per registrare la mia unità di lavoro con un codice simile a questo:

     unityContainer .RegisterType( new PerCallContextLifeTimeManager(), new InjectionConstructor()); 

    Spero che salvi qualcuno un po ‘di tempo.

    Soluzione pulita, ma ogni istanza di LifetimeManager deve utilizzare una chiave univoca anziché una costante:

     private string _key = string.Format("PerCallContextLifeTimeManager_{0}", Guid.NewGuid()); 

    Altrimenti se si ha più di un object registrato con PerCallContextLifeTimeManager, condividono la stessa chiave per accedere a CallContext e non si restituirà l’object previsto.

    Vale anche la pena implementare RemoveValue per garantire che gli oggetti vengano ripuliti:

     public override void RemoveValue() { CallContext.FreeNamedDataSlot(_key); } 

    Mentre questo va bene chiamando questo PerCallContextLifeTimeManager, sono abbastanza sicuro che questo non è “sicuro” per essere considerato un LifeTimeManager per richiesta di ASP.Net.

    Se ASP.Net esegue il thread-swap, l’ unica cosa che viene trasferita al nuovo thread attraverso CallContext è l’attuale HttpContext: qualsiasi altra cosa che si archivia in CallContext non sarà più disponibile. Questo significa che sotto il carico pesante il codice sopra potrebbe avere risultati non previsti – e immagino che sarebbe un vero dolore rintracciare il perché!

    L’unico modo “sicuro” per farlo è con HttpContext.Current.Items, o qualcosa del tipo:

     public class PerCallContextOrRequestLifeTimeManager : LifetimeManager { private string _key = string.Format("PerCallContextOrRequestLifeTimeManager_{0}", Guid.NewGuid()); public override object GetValue() { if(HttpContext.Current != null) return GetFromHttpContext(); else return GetFromCallContext(); } public override void SetValue(object newValue) { if(HttpContext.Current != null) return SetInHttpContext(); else return SetInCallContext(); } public override void RemoveValue() { } } 

    Questo ovviamente significa prendere dipendenza da System.Web 🙁

    Molte più informazioni su questo disponibili a:

    http://piers7.blogspot.com/2005/11/threadstatic-callcontext-and_02.html

    Grazie per il tuo contributo,

    Ma la domanda proposta di implementazione ha due inconvenienti, uno dei quali è un bug serio come già affermato da Steven Robbins nella sua risposta e Micah Zoltu in un commento .

    1. Il contesto di chiamata non può essere garantito da asp.net per una singola richiesta. Sotto carico, può passare a un altro, causando l’interruzione proposta di implementazione.
    2. Non gestisce il rilascio di dipendenze alla fine della richiesta.

    Attualmente, il pacchetto Nuget Unity.Mvc fornisce un PerRequestLifetimeManager per eseguire il lavoro. Non dimenticare di registrare il suo UnityPerRequestHttpModule associato nel codice di bootstrap, altrimenti non verrà gestito neanche il rilascio delle dipendenze.

    Utilizzando bootstrap

     DynamicModuleUtility.RegisterModule(typeof(UnityPerRequestHttpModule)); 

    oppure in web.config system.webServer/modules

      

    Sembra che la sua attuale implementazione sia adatta anche ai moduli web. E non dipende nemmeno da MVC. Sfortunatamente, il suo assemblaggio fa, causa di alcune altre classi che contiene.

    Attenzione, nel caso in cui si utilizzi un modulo http personalizzato utilizzando le dipendenze risolte, è ansible che siano già disponibili nel modulo EndRequest . Dipende dall’ordine di esecuzione del modulo.