Un DbContext per richiesta in ASP.NET MVC (senza contenitore IOC)

Ci scusiamo se questo è già stato risolto, ma come si garantisce un Entity Framework DbContext per richiesta se non si utilizza un contenitore IOC? (Le risposte che ho incontrato finora riguardano le soluzioni di container IOC.)

Sembra che la maggior parte delle soluzioni si agganci al dizionario HttpContext.Current.Items , ma come si garantisce lo smaltimento di DbContext al termine della richiesta? (O lo smaltimento non è assolutamente necessario con un DbContext EF?)

modificare

Attualmente sto creando un’istanza e disponendo il mio DbContext nei miei controller, ma ho anche diverse istanze separate del mio DbContext in ActionFilters e il mio MembershipProvider (e ho appena notato, anche un paio di validatori). Quindi, ho pensato che fosse una buona idea centralizzare l’istanziazione e l’archiviazione del mio DbContext per ridurre il sovraccarico.

Vorrei utilizzare il metodo BeginRequest / EndRequest, questo aiuta a garantire che il tuo contesto sia smaltito correttamente quando la richiesta è finita.

 protected virtual void Application_BeginRequest() { HttpContext.Current.Items["_EntityContext"] = new EntityContext(); } protected virtual void Application_EndRequest() { var entityContext = HttpContext.Current.Items["_EntityContext"] as EntityContext; if (entityContext != null) entityContext.Dispose(); } 

E nella tua class EntityContext …

 public class EntityContext { public static EntityContext Current { get { return HttpContext.Current.Items["_EntityContext"] as EntityContext; } } } 

So che questa non è una domanda recente, ma pubblicherò comunque la mia risposta, perché credo che qualcuno possa ritenerla utile.

Come probabilmente molti altri, ho seguito i passaggi indicati nella risposta accettata. Sì, funziona. TUTTAVIA , c’è una presa:

Metodi BeginRequest () e EndRequest () si triggersno ogni volta che viene effettuata una richiesta , ma non solo per le pagine aspx, ma per TUTTO IL CONTENUTO STATICO! Detto questo, se usi il codice sopra menzionato e hai sulla tua pagina diciamo 30 immagini, stai ripetendo l’istanziazione del tuo dbcontext 30 volte!

La soluzione per questo è usare una class wrapping per recuperare il contesto, qualcosa del genere:

 internal static class ContextPerRequest { internal static DB1Entities Current { get { if (!HttpContext.Current.Items.Contains("myContext")) { HttpContext.Current.Items.Add("myContext", new DB1Entities()); } return HttpContext.Current.Items["myContext"] as DB1Entities; } } } 

E poi per lo smaltimento

 protected void Application_EndRequest(object sender, EventArgs e) { var entityContext = HttpContext.Current.Items["myContext"] as DB1Entities; if (entityContext != null) entityContext.Dispose(); } 

Questa modifica garantisce l’istanziazione e la disposizione del contesto solo una volta per richiesta e solo quando necessario. La risposta selezionata istanzia il contesto ogni volta.

Nota: DB1Entities è derivato da DbContext (generato da VS). Probabilmente vorrai modificarlo con il tuo nome di contesto;)

Nota 2: in questo esempio sto lavorando con un solo dbcontext. Se hai bisogno di lavorare con più, dovresti modificare questo codice in base alle tue esigenze. Non considerarlo come una soluzione definitiva ai problemi del mondo, perché certamente non è un prodotto finale. È inteso solo per dare un suggerimento, come può essere raggiunto in un modo molto semplice.

Nota 3: Lo stesso approccio può essere utilizzato anche in diverse situazioni, ad esempio quando si desidera condividere un’istanza di SqlConnection o qualsiasi altra … Questa soluzione non è esclusiva per l’object DbContext, né per Entity framework.

Un modo sarebbe sottoscrivere l’evento Application_BeginRequest , iniettare DbContext nell’attuale HttpContext e nel recupero Application_EndRequest da HttpContext e dispose. Qualunque cosa nel mezzo (che è praticamente tutto :-)) potrebbe recuperare DbContext dall’attuale HttpContext e usarlo. E sì, dovresti sbarazzartene. E comunque c’è qualche ragione per cui non usi un framework DI che già fa questo per te tra le altre cose utili?

Piccola aggiunta per la risposta di Chad Moran. È ispirato alle note di walther. Per evitare l’inizializzazione del contesto per il contenuto statico, dovremmo controllare il gestore del percorso corrente (questo esempio solo per MVC):

 protected virtual void Application_BeginRequest() { var routeData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(this.Context)); if (routeData != null && routeData.RouteHandler is MvcRouteHandler) { HttpContext.Current.Items["_EntityContext"] = new EntityContext(); } } 

Se si implementa IDisposable nel controller e si dispone il contesto nel metodo di disposizione e si crea un nuovo contesto nel costruttore di controller, si dovrebbe essere sicuri poiché il controller viene istanziato per ogni richiesta. Non vedo, tuttavia, perché vorresti farlo? … Dovresti usare DI, o creare un factory di contesto con una istanza di contesto statica. Se non usi un’istanza (ne fai una per ogni richiesta), ad un certo punto avrai dei problemi. Il problema con il contesto non esposto è che EF memorizza i dati nella cache nel contesto e se qualche altra istanza di contesto modifica qualcosa nel DB che è già memorizzato nella cache in un altro contesto, si ha uno stato non coerente. Prima che DI diventasse così popolare, avevo sempre un’istanza statica di contesto da qualche parte nell’applicazione, e questo è molto più veloce e più sicuro che ogni richiesta faccia il suo contesto, ma è necessario implementare un codice di controllo dello stato che assicuri quel contesto la connessione a db è ok … Ci sono molte migliori soluzioni a questo problema, e la cosa migliore è usare qualche framework DI. Vorrei raccomandare Ninject in combinazione con MVCTurbine, è facile da configurare ed è ansible aggiungerlo tramite NuGet.

Il pendio scivoloso qui sta avendo uno stato inconsistente. Se la tua app avrà più utenti, e potrebbero potenzialmente modificare i dati contemporaneamente, potresti iniziare a riscontrare problemi con l’integrità dei dati se mantieni un singolo contesto.