2013-04-26 7 views
5

Ich projiziere LINQ zu SQL-Ergebnissen zu stark typisierten Klassen: Parent und Child. Der Performance-Unterschied zwischen diesen beiden Abfragen ist groß:Eins-zu-viele projizierte LINQ-Abfrage wird wiederholt ausgeführt

langsame Anfragen - von der Datacontext-Anmeldung zeigt, dass ein separater Aufruf die db wird

für jeden Elternteil gemacht wird
var q = from p in parenttable 
     select new Parent() 
     { 
      id = p.id, 
      Children = (from c in childtable 
         where c.parentid = p.id 
         select c).ToList() 
     } 
return q.ToList() //SLOW 

Schnelle Abfrage - Protokollierung von der Datacontext zeigt eine einzige db Hit-Abfrage, die alle erforderlichen Daten

var q = from p in parenttable 
     select new Parent() 
     { 
      id = p.id, 
      Children = from c in childtable 
         where c.parentid = p.id 
         select c 
     } 
return q.ToList() //FAST 

ich möchte zwingen, LINQ gibt die Single-Abfrage Stil des zweiten Beispiels zu verwenden, aber die Eltern-Klassen mit ihren Kinder-Objekte direkt zu füllen. Andernfalls ist die Children-Eigenschaft eine IQuerierable<Child>, die abgefragt werden muss, um das Child-Objekt verfügbar zu machen.

Die referenzierten Fragen scheinen nicht meine Situation zu adressieren. Die Verwendung von db.LoadOptions funktioniert nicht. vielleicht muss der Typ eine TEntity sein, die beim DataContext registriert ist.

DataLoadOptions options = new DataLoadOptions(); 
    options.LoadWith<Parent>(p => p.Children); 
    db.LoadOptions = options; 

Bitte beachten Sie: Eltern und Kind sind einfache Typen, nicht Table<TEntity> Typen. und es gibt keine kontextuelle Beziehung zwischen Eltern und Kind. Die Unterabfragen sind Ad-hoc. Die Crux des Problems: Im 2. LINQ-Beispiel implementiere ich IQueriable Anweisungen und rufe nicht ToList() Funktion auf und aus irgendeinem Grund weiß LINQ, wie man eine einzelne Abfrage generiert, die alle erforderlichen Daten abrufen kann. Wie befülle ich meine Ad-hoc-Projektion mit den tatsächlichen Daten, wie sie in der ersten Abfrage durchgeführt werden? Auch wenn mir jemand helfen könnte, meine Frage besser zu formulieren, würde ich es begrüßen.

+0

Wie kann 'Child' ein" einfacher Typ "sein? Es sollte ein abgebildeter Typ sein. Und ist "Elterntabelle" eine "Tabelle" oder das Ergebnis einer vorherigen Linq-Abfrage? Einige linq-Anweisungen können dazu führen, dass L2S von der Verknüpfung zu N + 1 wechselt. –

+1

Doppelte Marker: Kennen Sie den Unterschied zwischen Entity Framework und Linq zu SQL? –

+0

@GertArnold Während der LINQ-Teil von EF/L2S häufig ähnliche Fragen funktional dupliziert, glaube ich, dass Sie in diesem Fall völlig korrekt sind. Sie sind keine Duplikate. Wiedereröffnung. –

Antwort

0

Sie müssen die richtigen Optionen für Ihre Daten laden.

options.LoadWith<Document>(d => d.Metadata); 

Blick auf this

P. S. Include nur für die LINQToEntity.

+0

Das sieht aus wie es sollte die Antwort sein, aber es hat nicht für mich funktioniert. Ich versuchte dlo.LoadWith (p => p.Children). immer noch mehrere db hits – Paul

+0

@Paul Sie müssen nicht verwenden Elterntabelle. Haben Sie Entität Parent von LINQ2SQL generiert? Dann enthält Ihre Entität Children bereits, wenn Sie Parent aus dem Kontext anfordern. –

0

Die zweite Abfrage ist schnell genau, weil Children wird nicht bevölkert.

Und der erste ist langsam nur weil Kinder wird bevölkert.

Wählen Sie die, die am besten zu Ihren Bedürfnissen passt, Sie können einfach nicht ihre Funktionen zusammen haben!

EDIT:

Als @Servy sagt:

In Ihrer zweiten Abfrage, die Sie tatsächlich keine Informationen über die Kinder zu holen. Sie haben die Abfragen erstellt, aber Sie haben sie nicht ausgeführt, um die Ergebnisse dieser Abfragen zu erhalten. Wenn Sie die Liste iterieren und anschließend die Children-Auflistung jedes Elements durchlaufen würden, würde dies so lange dauern wie die erste Abfrage.

1

Der schnellste Weg, den ich gefunden habe, um dies zu erreichen, ist eine Abfrage, die alle Ergebnisse zurückgibt und dann alle Ergebnisse gruppiert. Stellen Sie sicher, dass Sie eine .ToList() für die erste Abfrage ausführen, damit die zweite Abfrage nicht viele Aufrufe ausführt.

Hier sollte r haben, was Sie mit nur einer einzigen DB-Abfrage erreichen möchten.

  var q = from p in parenttable 
        join c in childtable on p.id equals c.parentid 
        select c).ToList(); 
      var r = q.GroupBy(x => x.parentid).Select(x => new { id = x.Key, Children=x }); 
+1

Anstatt einen Join und dann eine Gruppe zu verwenden, verwenden Sie einfach einen GroupJoin. – Servy

5

Es ist wichtig, dass LINQ-Abfragen in verzögerte Ausführung verlassen zu erinnern. In Ihrer zweiten Abfrage erhalten Sie keine Informationen über die Kinder. Sie haben die Abfragen erstellt, aber Sie haben sie nicht ausgeführt, um die Ergebnisse dieser Abfragen zu erhalten. Wenn Sie die Liste iterieren und dann die Children Sammlung jedes Elements iterieren würden, würde dies so lange dauern wie die erste Abfrage.

Ihre Abfrage ist auch von Natur aus sehr ineffizient. Sie verwenden eine verschachtelte Abfrage, um eine Join-Beziehung darzustellen. Wenn Sie stattdessen eine Join verwenden, kann die Abfrage sowohl vom Abfrageprovider als auch von der Datenbank entsprechend optimiert werden, um sie viel schneller auszuführen. Möglicherweise müssen Sie auch die Indizes für Ihre Datenbank anpassen, um die Leistung zu verbessern. So könnte der Join aussehen:

var q = from p in parenttable 
     join child in childtable 
     on p.id equals child.parentid into children 
     select new Parent() 
     { 
      id = p.id, 
      Children = children.ToList(), 
     } 
return q.ToList() //SLOW 
+0

Ich weiß, die Frage bezieht sich auf LINQ to SQL, aber ich dachte, ich würde erwähnen, wenn Sie Entity Framework verwenden, ist die 'ToList()' nicht erforderlich für 'Kinder'. –

Verwandte Themen