2012-05-27 13 views
18

Ho alcune domande sulla durata desiderata di un contesto Entity Framework in un'applicazione ASP.NET MVC. Non è meglio mantenere vivo il contesto per il minor tempo possibile?Domande su Entity Framework Context Lifetime

Si consideri il seguente azione di controllo:

public ActionResult Index() 
{ 
    IEnumerable<MyTable> model; 

    using (var context = new MyEntities()) 
    { 
     model = context.MyTable; 
    } 

    return View(model); 
} 

Il codice di cui sopra non funziona perché il contesto Entity Framework va portata mentre la vista visualizza la pagina. Come potrebbero gli altri strutturare il codice sopra?

+0

'model = context.MyTable.ToList()' - 'ToList()' eseguirà la query. Nel tuo caso IQueryable infatti non funzionerebbe al di fuori dell'ambito del contesto. – Andrei

risposta

41

Andiamo controverso!

Non sono d'accordo con il generale MVC + EF consenso che mantenere in vita un contesto in tutta l'intera richiesta è una buona cosa per una serie di motivi:

scarso aumento delle prestazioni Sai come costosa la creazione di un il nuovo contesto del database è? Bene..."Un DataContext è leggero e non è costoso per creare" che è da MSDN

Prendi il CIO sbagliato e sembrerà bene .. fino a quando si va in diretta Se si imposta il contenitore CIO di disporre di il tuo contesto per te e te lo sbagli, davvero ti sbagli davvero. Io ho due volte ora ho visto enormi perdite di memoria create da un contenitore IoC che non sempre smaltiscono correttamente un contesto. Non ti accorgerai di averlo impostato erroneamente fino a quando i tuoi server non si saranno sbriciolati durante i normali livelli di utenti concorrenti. Non accadrà nello sviluppo, quindi esegui alcuni test di carico!

Caricamento pigro accidentale Si restituisce un IQueryable dei tuoi articoli più recenti in modo che sia possibile elencarli nella tua home page. Un giorno a qualcun altro viene chiesto di mostrare il numero di commenti accanto al rispettivo articolo. In modo da aggiungere un po 'di semplice codice alla vista per mostrare il numero di commenti in questo modo ...

@foreach(var article in Model.Articles) { 
    <div> 
     <b>@article.Title</b> <span>@article.Comments.Count() comments</span> 
    </div> 
} 

guarda bene, funziona bene. Ma in realtà non hai incluso i commenti nei tuoi dati restituiti, quindi ora questo farà una nuova chiamata al database per ogni articolo nel ciclo. SELEZIONA N + 1 problema. 10 articoli = 11 chiamate al database. Ok, quindi il codice è sbagliato ma è un errore facile farlo in modo che accada.

È possibile evitare ciò chiudendo il contesto nel proprio livello dati. Ma il codice non si romperà con una NullReferenceException sull'articolo.Comments.Count()? Sì, così sarà costretto a modificare il livello Data per ottenere i dati necessari per il livello Visualizza. Questo è come dovrebbe essere.

Codice odore C'è solo qualcosa di sbagliato nel colpire il database dalla Vista. Sai che un IQueryable in realtà non ha colpito il database ancora giusto, quindi dimentica quell'oggetto. Assicurati che il tuo database sia colpito prima che lasci il tuo livello dati.

Quindi la risposta

Il codice dovrebbe essere (a mio parere) come questo

DataLayer:

public List<Article> GetArticles() 
{ 
    List<Article> model; 

    using (var context = new MyEntities()) 
    { 
     //for an example I've assumed your "MyTable" is a table of news articles 
     model = (from mt in context.Articles 
       select mt).ToList(); 
     //data in a List<T> so the database has been hit now and data is final 
    } 

    return model; 
} 

Controller:

public ActionResult Index() 
{ 
    var model = new HomeViewModel(); //class with the bits needed for you view 
    model.Articles = _dataservice.GetArticles(); //irrelevant how _dataService was intialised 
    return View(model); 
} 

volta che avete fatto questo e capisci questo allora forse yo Puoi iniziare a sperimentare con il contesto di gestire un contenitore IoC, ma sicuramente non prima. Testa il mio avvertimento - Ho visto due errori su larga scala :)

Ma sinceramente fai quello che ti piace, la programmazione è divertente e dovrebbe essere una questione di preferenza. Ti sto solo raccontando il mio. Ma qualunque cosa tu faccia, non iniziare a utilizzare il contesto IoC per controller o per richiesta solo perché "tutti i ragazzi fantastici lo stanno facendo". Fallo perché tieni veramente a cuore i suoi benefici e capisci come è fatto correttamente.

+3

Penso che valga la pena citare l'intero paragrafo di MSDN: "In generale, un'istanza DataContext è progettata per durare per una 'unità di lavoro', tuttavia l'applicazione definisce tale termine. Un DataContext è leggero e non è costoso da creare. LINQ all'applicazione SQL crea istanze DataContext nell'ambito del metodo o come membro di classi di breve durata che rappresentano un insieme logico di operazioni correlate del database. " –

+1

@BritishDeveloper: la domanda riguardava DbContext dallo spazio dei nomi System.Data.Entity (classe dbcontext di entity framework). Non ho trovato nulla di "leggerezza" nella descrizione di questo contesto in [MSDN] (http://msdn.microsoft.com/en-us/library/system.data.entity.dbcontext%28v=vs.113%29. aspx). Hai altre prove? – vk5880

+0

Ah, questo è stato 2 anni fa, cba per trovare più prove per voi. Tuttavia, ho esperienza professionale nell'utilizzo di questo metodo negli ultimi anni, su siti di grandi dimensioni, senza problemi o problemi di prestazioni. – BritishDeveloper

2

In primo luogo, è necessario considerare l'accesso al database a classi separate.

In secondo luogo, la mia soluzione preferita è utilizzare "un contesto per richiesta" (se si utilizza MVC, credo che sia un contesto per controller).

modificare richiesto:

Date un'occhiata a questa risposta, forse vi aiuterà anche voi. Si prega di notare che sto usando webforms, quindi non posso verificarlo in MVC al momento, ma potrebbe essere utile per te o almeno darti alcuni suggerimenti. https://stackoverflow.com/a/10153406/1289283

Alcuni esempio di utilizzo di questo DbContext:

public class SomeDataAccessClass 
{ 
    public static IQueryable<Product> GetAllProducts() 
    { 
     var products = from o in ContextPerRequest.Current.Products 
         select o; 
     return products; 
    } 
} 

allora si può fare qualcosa di simile:

public ActionResult Index() 
{ 
    var products = SomeDataAccessClass.GetProducts(); 
    return View(products); 
} 

Semplice, no? Non devi più preoccuparti di eliminare il tuo contesto, scrivi solo il codice che ti serve davvero.

Alcune persone amano arricchire ulteriormente le cose aggiungendo il modello UnitOfWork, o forse i contenitori IoC ... Ma questo approccio mi piace di più per la sua semplicità.

+0

Grazie, ma sto cercando di capire come le persone strutturerebbero il codice in modo che ci sia un solo contesto per richiesta?Dove lo creerei e come potrei assicurarmi che sia smaltito in modo tempestivo quando viene effettuata la richiesta? –

5

Sono d'accordo con un contesto per ogni richiesta, normalmente facciamo legandosi contesto .InRequestScope utilizzando Ninject, che funziona davvero bene, è:

Bind<MyContext>().ToSelf().InRequestScope(); 

anche la sua davvero buona pratica per enumerare il set più vicino alla domanda possibile:

public ActionResult Index() 
{ 
    IEnumerable<MyTable> model; 

    using (var context = new MyEntities()) 
    { 
     model = (from mt in context.MyTable 
       select mt).ToArray(); 
    } 
    return View(model); 
} 

questo vi aiuterà ad evitare di aumentare involontariamente la query dal vostro punto di vista.

1

Puoi utilizzare il metodo .ToList() estensione di LINQ in quanto tale:

public ActionResult Index() 
{ 
    IEnumerable<MyTable> model; 

    using (var context = new MyEntities()) 
    { 
     model = (from mt in context.MyTable 
       select mt).ToList(); 
    } 
    return View(model); 
} 
+0

Sì, ma non risolve potenziali problemi di prestazioni ricreando il contesto più volte durante una singola richiesta HTTP. Ecco perché sto cercando esempi su come gli altri stanno facendo questo. –

+1

Hai misurato l'impatto sulle prestazioni? Si adatta alle tue aspettative/requisiti? –

+0

Non ho misurato le prestazioni. Sto solo cercando di capire come gli altri hanno a che fare con questo particolare aspetto dell'EF. Sto avendo difficoltà a trovare molti esempi. –