2012-09-20 4 views
6

ich die Loading Related Entities Beitrag von der Entity Framework Team gelesen und habe ein bisschen von dem letzten Absatz verwirrt:Ist LINQ mit einem skalaren Ergebnis der träges Laden auslösen

Manchmal ist es nützlich, wie viele Einheiten zu wissen, beziehen sich auf eine andere Entität in der Datenbank, ohne dass tatsächlich die Kosten für das Laden all dieser Entitäten entstehen. Dazu kann die Query-Methode mit der LINQ Count-Methode verwendet werden. Zum Beispiel:

using (var context = new BloggingContext()) 
{ 
    var blog = context.Blogs.Find(1); 

    // Count how many posts the blog has 
    var postCount = context.Entry(blog) 
          .Collection(b => b.Posts) 
          .Query() 
          .Count(); 
} 

Warum die Query + Count Methode hier benötigt?
Können wir nicht einfach die Methode COUNT von LINQ verwenden?

var blog = context.Blogs.Find(1); 
var postCount = blog.Posts.Count(); 

Will, der den verzögertes Laden und die ganze Sammlung auslösen wird in den Speicher geladen wird und nur als ich meinen gewünschten Skalarwert bekommen?

Antwort

2

Die erste Methode ist der Laden nicht alle Zeilen da die Count Verfahren von einem IQueryable aufgerufen wird, aber das zweite Verfahren alle Zeilen wird geladen, da sie aus einem ICollection aufgerufen wird.

Ich habe einige Tests durchgeführt, um es zu überprüfen. Ich testete es mit Table1 und Table2, wobei Table1 das PK "Id" und Table2 das FK "Id1" (1: N) hat. Ich habe EF Profiler von hier http://efprof.com/ verwendet.

Erste Methode:

var t1 = context.Table1.Find(1); 

var count1 = context.Entry(t1) 
         .Collection(t => t.Table2) 
         .Query() 
         .Count(); 

Nein Select * From Table2:

SELECT TOP (2) [Extent1].[Id] AS [Id] 
FROM [dbo].[Table1] AS [Extent1] 
WHERE [Extent1].[Id] = 1 /* @p0 */ 

SELECT [GroupBy1].[A1] AS [C1] 
FROM (SELECT COUNT(1) AS [A1] 
     FROM [dbo].[Table2] AS [Extent1] 
     WHERE [Extent1].[Id1] = 1 /* @EntityKeyValue1 */) AS [GroupBy1] 

Zweite Methode:

var t1 = context.Table1.Find(1); 
var count2 = t1.Table2.Count(); 

Table2 in den Speicher geladen wird:

SELECT TOP (2) [Extent1].[Id] AS [Id] 
FROM [dbo].[Table1] AS [Extent1] 
WHERE [Extent1].[Id] = 1 /* @p0 */ 

SELECT [Extent1].[Id] AS [Id], 
     [Extent1].[Id1] AS [Id1] 
FROM [dbo].[Table2] AS [Extent1] 
WHERE [Extent1].[Id1] = 1 /* @EntityKeyValue1 */ 

Warum passiert das? Das Ergebnis von Collection(t => t.Table2) ist eine Klasse, die ICollection implementiert, aber es lädt nicht alle Zeilen und hat eine Eigenschaft namens IsLoaded. Das Ergebnis des Query-Verfahrens ist ein IQueryable, und dies ermöglicht das Aufrufen von Count ohne das Vorladen von Zeilen.

Das Ergebnis von t1.Table2 ist ein ICollection und es lädt alle Zeilen, um die Anzahl zu erhalten. Übrigens, selbst wenn Sie nur t1.Table2 verwenden, ohne nach der Anzahl zu fragen, werden Zeilen in den Speicher geladen.

+0

Danke Amiram, also mein Gedanke war richtig, löst die Sammlung Eigenschaft Getter die Lazy-Loading. Ich akzeptiere es. – gdoron

6

Sie erhalten den gewünschten Skalarwert in Bot-Fällen. Aber bedenke den Unterschied in dem was passiert.

Mit .Query().Count() führen Sie eine Abfrage in der Datenbank des Formulars SELECT COUNT(*) FROM Posts aus und weisen diesen Wert Ihrer Integer-Variablen zu.

Mit .Posts.Count, Sie laufen (so etwas wie) SELECT * FROM Posts auf der Datenbank (schon viel teurer). Jede Zeile des Ergebnisses wird dann Feld für Feld Ihrem C# -Objekttyp zugeordnet, wenn die Auflistung aufgelistet wird, um Ihre Anzahl zu ermitteln. Wenn Sie auf diese Weise nach der Zählung fragen, erzwingen Sie, dass alle Daten geladen werden, sodass C# zählen kann, wie viel vorhanden ist.

Hoffentlich ist es offensichtlich, dass es viel effizienter ist, die Datenbank nach der Anzahl der Zeilen zu fragen (ohne tatsächlich alle Zeilen zurückzugeben)!

+0

Ich kenne den Unterschied zwischen den beiden, ich frage, warum der erste nicht das Lazyloading auslöst, während der zweite ist. Ich denke, die Antwort ist die Tatsache, dass wenn Sie den Getter der Sammlung verwenden, löst das Lazyloading. Habe ich recht? – gdoron

+0

@AmiramKorach. Wenn Sie denken, dass es falsch ist, schreiben Sie bitte eine vollständige Antwort, warum. Danke Kumpel. – gdoron

1

Die erste Lösung löst das verzögerte Laden nicht aus, da sie höchstwahrscheinlich niemals direkt auf die Auflistungseigenschaft zugreift. Die Collection Methode akzeptiert Expression, nicht nur delegieren. Es wird nur verwendet, um den Namen der Eigenschaft abzurufen, die dann für den Zugriff auf Zuordnungsinformationen und die Erstellung korrekter Abfragen verwendet wird.

Auch wenn es auf die Auflistungseigenschaft zugreifen würde, könnte es die gleiche Strategie wie andere interne Teile von EF (z. B. Validierung) verwenden, die temporäres Laden vorübergehend deaktiviert, bevor auf Navigationseigenschaften zugreifen, unerwartetes verzögertes Laden zu vermeiden.

Btw. Dies ist eine große Verbesserung im Gegensatz zur ObjectContext-API, bei der das Erstellen einer Abfrage den Zugriff auf die Navigationseigenschaft erforderte und somit ein verzögertes Laden auslösen konnte.

Es gibt einen weiteren Unterschied zwischen diesen beiden Ansätzen:

  • Die erste führt immer Abfrage-Datenbank und gibt Anzahl der Elemente in der Datenbank
  • Die zweite Abfrage zur Datenbank wird nur ausgeführt, wenn alle Einzelteile laden und gibt dann die Anzahl der Elemente in der Anwendung zurück, ohne den Status in der Datenbank zu überprüfen

Als dritte recht interessante Option können Sie zusätzliche Ladevorgänge verwenden.The implementation by Arthur Vickers zeigt, wie mithilfe der Navigationseigenschaft die Anzahl aus der Datenbank abgerufen werden kann, ohne dass Elemente mit Lazy Loading geladen werden.

+0

+1. Danke für deine Antwort und Zeit! – gdoron

Verwandte Themen