2012-05-27 6 views
18

Ich habe einige Fragen über die gewünschte Lebensdauer eines Entity Framework-Kontext in einer ASP.NET MVC-Anwendung. Ist es nicht am besten, den Kontext für die kürzest mögliche Zeit am Leben zu erhalten?Fragen zu Entity Framework Kontext Lebensdauer

Betrachten Sie die folgende Controller-Aktion:

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

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

    return View(model); 
} 

Der obige Code wird nicht funktionieren, weil die Entity Framework Kontext außerhalb des Gültigkeitsbereichs gegangen ist, während die Ansicht, die die Seite macht. Wie würden andere den obigen Code strukturieren?

+0

'model = context.MyTable.ToList()' - 'ToList()' wird Ihre Abfrage ausführen. In Ihrem Fall würde IQueryable tatsächlich nicht außerhalb des Kontextbereichs arbeiten. – Andrei

Antwort

41

wir umstritten bekommen!

Ich bin nicht einverstanden mit dem allgemeinen MVC + EF Konsens darüber, dass ein Zusammenhang in der gesamten Anfrage am Leben zu halten, ist eine gute Sache für eine Reihe von Gründen:

Low Leistungssteigerung Wissen Sie, wie teuer eine Schaffung neuer Datenbankkontext ist? Gut...„Ein Datacontext ist leicht und ist nicht teuer zu schaffen“, das ist von MSDN

Holen Sie sich das IoC falsch und es wird scheinen gut .., bis Sie online gehen Wenn Sie Ihre IoC-Container einrichten zu entsorgen Dein Kontext für dich und du verstehst falsch, du liegst wirklich falsch. Ich habe zweimal jetzt gesehen massive Speicherlecks von einem IoC-Container erstellt nicht immer einen Kontext richtig entsorgen. Sie werden nicht bemerken, dass Sie es falsch eingerichtet haben, bis Ihre Server während normaler Benutzerlevel zusammenbrechen. Es wird nicht in der Entwicklung passieren, also einige Belastungstests!

Versehentliches Lazy Loading Sie geben ein IQueryable Ihrer letzten Artikel zurück, damit Sie sie auf Ihrer Homepage auflisten können. Eines Tages wird jemand anderes gebeten, die Anzahl der Kommentare neben dem jeweiligen Artikel anzuzeigen. So fügen sie ein einfaches Stück Code auf den View wie so die Anzahl der Kommentare zu zeigen ...

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

sieht gut aus, funktioniert gut. Sie haben die Kommentare jedoch nicht in die zurückgegebenen Daten aufgenommen. Dadurch wird nun für jeden Artikel in der Schleife ein neuer Datenbankaufruf ausgeführt. Wählen Sie N + 1 Problem. 10 Artikel = 11 Datenbankaufrufe. Okay, der Code ist falsch, aber es ist ein leichter Fehler, so dass es passieren wird.

Sie können dies verhindern, indem Sie Ihren Kontext in Ihrer Datenschicht herunterfahren. Aber bricht der Code nicht mit einer NullReferenceException für article.Comments.Count()? Ja, es wird Sie zwingen, die Datenschicht zu bearbeiten, um die Daten zu erhalten, die für die Ansichtsebene benötigt werden. So sollte es sein.

Code Geruch Es ist nur etwas falsch, die Datenbank von Ihrem View zu treffen. Sie wissen, dass ein IQueryable die Datenbank noch nicht wirklich getroffen hat, also vergessen Sie dieses Objekt. Stellen Sie sicher, dass Ihre Datenbank getroffen wird, bevor sie Ihre Datenebene verlässt.

So ist die Antwort

sollte Ihr Code (meiner Meinung nach) wie dieses

Datalayer sein:

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

Sobald Sie getan haben, das und versteh das dann vielleicht yo Sie können damit beginnen, mit einem IoC-Containerstruktur-Kontext zu experimentieren, aber definitiv nicht vorher. Kopf meine Warnung - Ich habe zwei große Ausfälle gesehen :)

Aber ehrlich gesagt, was Sie wollen, Programmierung macht Spaß und sollte eine Frage der Vorliebe sein. Ich sage dir nur meine. Aber was auch immer Sie tun, fangen Sie nicht an, den IoC-Kontext pro Controller oder pro Anfrage zu verwenden, nur weil "all die coolen Kids es tun". Tun Sie es, weil Sie wirklich wirklich auf seine Vorteile achten und verstehen, wie es richtig gemacht wird.

+3

Ich denke, es lohnt sich, den ganzen Absatz von MSDN zu zitieren: "Im Allgemeinen ist eine DataContext-Instanz für eine 'Arbeitseinheit' konzipiert, aber Ihre Anwendung definiert diesen Begriff. Ein DataContext ist leicht und nicht teuer in der Erstellung LINQ to SQL-Anwendung erstellt DataContext-Instanzen im Methodenbereich oder als Mitglied von kurzlebigen Klassen, die einen logischen Satz verwandter Datenbankoperationen darstellen. " –

+1

@BritishDeveloper: Die Frage war über DbContext aus System.Data.Entity Namespace (Entity Framework dbcontext Klasse). Ich habe nichts gefunden "Leichtgewicht" in der Beschreibung dieses Zusammenhangs bei [MSDN] (http://msdn.microsoft.com/en-us/library/system.data.entity.dbcontext%28v=vs.113%29. aspx). Hast du andere Beweise? – vk5880

+0

Ah, das war vor 2 Jahren, cba, um mehr Beweise für dich zu finden. Ich habe jedoch professionelle Erfahrung mit dieser Methode in den letzten Jahren, auf großen Websites, ohne Probleme oder Leistungsbedenken. – BritishDeveloper

2

Zunächst sollten Sie in Erwägung ziehen, den Datenbankzugriff auf separate Klassen zu beschränken.

Zweitens ist meine Lieblingslösung, um "einen Kontext pro Anfrage" zu verwenden (wenn Sie MVC verwenden, glaube ich, es ist ein Kontext pro Controller).

Gewünscht edit:

Werfen Sie einen Blick auf diese Antwort, vielleicht wird es auch Ihnen helfen. Bitte beachten Sie, dass ich Webformulare verwende, daher kann ich diese im Moment in MVC nicht überprüfen, aber es kann hilfreich für Sie sein oder Ihnen zumindest einige Hinweise geben. https://stackoverflow.com/a/10153406/1289283

Einige Beispiele Verwendung dieses DbContext:

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

Dann können Sie etwas tun:

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

Einfach, nicht wahr? Sie müssen sich nicht mehr darum sorgen, Ihren Kontext zu verwerfen, Sie schreiben nur den Code, den Sie wirklich brauchen.

Manche Leute mögen es, etwas mehr aufzupeppen, indem sie das UnitOfWork-Muster oder vielleicht IoC-Container hinzufügen ... Aber ich mag diesen Ansatz mehr wegen seiner Einfachheit.

+0

Danke, aber ich versuche herauszufinden, wie die Leute den Code strukturieren würden, so dass es nur einen Kontext pro Anfrage gibt?Wo würde ich es schaffen, und wie würde ich sicherstellen, dass es rechtzeitig erledigt wird, wenn die Anfrage erledigt wird? –

5

ich mit einem Kontext pro Antrag zustimmen, wir tun dies in der Regel durch den Kontext Bindung .InRequestScope mit Ninject, die wirklich gut funktioniert, ist:

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

auch seine wirklich gute Praxis, den Satz so nahe aufzuzählen zur Abfrage wie möglich zB:

Dies wird Ihnen helfen vermeiden vermeiden, die Abfrage unbeabsichtigt aus Ihrer Sicht zu erweitern.

1

Kann nutzen Sie .ToList() Erweiterungsmethode LINQ als solche:

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

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

Ja, das kann ich, aber das behebt potenzielle Leistungsprobleme nicht durch mehrfaches Neuerstellen des Kontexts während einer einzelnen HTTP-Anforderung. Deshalb suche ich nach Beispielen dafür, wie andere dies tun. –

+1

Haben Sie die Auswirkungen auf die Leistung gemessen? Passt es zu Ihren Erwartungen/Anforderungen? –

+0

Ich habe die Leistung nicht gemessen. Ich versuche nur herauszufinden, wie andere mit diesem besonderen Aspekt von EF umgehen. Ich habe Probleme, viele Beispiele zu finden. –

Verwandte Themen