2012-06-20 5 views
5

ich beheben hat den folgenden Code, der sie schlecht benimmt:Wie super langsam EF/LINQ-Abfrage Ausführen mehr SQL-Anweisungen

TPM_USER user = UserManager.GetUser(context, UserId); 
var tasks = (from t in user.TPM_TASK 
      where t.STAGEID > 0 && t.STAGEID != 3 && t.TPM_PROJECTVERSION.STAGEID <= 10 
      orderby t.DUEDATE, t.PROJECTID 
      select t); 

Die erste Zeile, wird UserManager.GetUser nur eine einfache Suche in der Datenbank, um die richtigen zu bekommen TPM_USER aufnehmen. Die zweite Zeile verursacht jedoch alle Arten von SQL-Chaos.

Als erstes werden hier zwei SQL-Anweisungen ausgeführt. Die erste packt jede einzelne Zeile in TPM_TASK, die diesem Benutzer verknüpft ist, die manchmal Zehntausende von Zeilen:

SELECT 
-- Columns 
FROM TPMDBO.TPM_USERTASKS "Extent1" 
INNER JOIN TPMDBO.TPM_TASK "Extent2" ON "Extent1".TASKID = "Extent2".TASKID 
WHERE "Extent1".USERID = :EntityKeyValue1 

Diese Abfrage dauert etwa 18 Sekunden auf Benutzer mit vielen Aufgaben. Ich würde erwarten, dass die WHERE-Klausel auch die STAGEID-Filter enthält, die die Mehrheit der Zeilen entfernen würde.

Als nächstes scheint es eine neue Abfrage für jedes TPM_PROJECTVERSION Paar in der Liste auszuführen oben:

SELECT 
-- Columns 
FROM TPMDBO.TPM_PROJECTVERSION "Extent1" 
WHERE ("Extent1".PROJECTID = :EntityKeyValue1) AND ("Extent1".VERSIONID = :EntityKeyValue2) 

Auch wenn diese Abfrage schnell ist, es mehr hundert Mal ausgeführt ist, wenn die Benutzer Aufgaben in einem ganzen Bündel hat von Projekten.

SELECT 
-- Columns 
FROM TPMDBO.TPM_USERTASKS "Extent1" 
INNER JOIN TPMDBO.TPM_TASK "Extent2" ON "Extent1".TASKID = "Extent2".TASKID 
INNER JOIN TPMDBO.TPM_PROJECTVERSION "Extent3" ON "Extent2".PROJECTID = "Extent3".PROJECTID AND "Extent2".VERSIONID = "Extent3".VERSIONID 
WHERE "Extent1".USERID = 5 and "Extent2".STAGEID > 0 and "Extent2".STAGEID <> 3 and "Extent3".STAGEID <= 10 

Die Abfrage oben in etwa 1 Sekunde laufen würde:

Die Abfrage Ich mag würde wäre so etwas wie aussehen zu erzeugen. Normalerweise könnte ich JOIN mit der Methode Include angeben. Dies scheint jedoch bei Eigenschaften nicht zu funktionieren. Mit anderen Worten, ich kann nicht:

from t in user.TPM_TASK.Include("TPM_PROJECTVERSION") 

Gibt es eine Möglichkeit, diese LINQ-Anweisung zu optimieren? Ich verwende .NET4 und Oracle als Backend-DB.

Lösung:

Diese Lösung unten auf Kirks Vorschläge basiert, und arbeitet seit context.TPM_USERTASK nicht direkt abgefragt werden können:

var tasks = (from t in context.TPM_TASK.Include("TPM_PROJECTVERSION") 
      where t.TPM_USER.Any(y => y.USERID == UserId) && 
      t.STAGEID > 0 && t.STAGEID != 3 && t.TPM_PROJECTVERSION.STAGEID <= 10 
      orderby t.DUEDATE, t.PROJECTID 
      select t); 

Es tut Ergebnis in einer verschachtelten SELECT anstatt TPM_USERTASK abfragt direkt, aber es scheint ziemlich effizient dennoch.

Antwort

4

Ja, Sie ziehen einen bestimmten Benutzer herunter und verweisen dann auf die Beziehung TPM_TASK. Es ist genau das, was es tun soll, dass es alle Aufgaben erledigt, die mit diesem Benutzer verbunden sind. Es gibt keine ORM-SQL-Übersetzung, wenn Sie es auf diese Weise tun. Sie erhalten einen Benutzer und dann alle Aufgaben in den Speicher und führen dann eine clientseitige Filterung durch. Dies wird alles mit Hilfe von Lazy-Loading durchgeführt, so dass das SQL außergewöhnlich ineffizient sein wird, da es nichts aufladen kann.

Stattdessen umschreiben Ihre Anfrage direkt gegen TPM_TASK und Filter gegen den Benutzer zu gehen:

var tasks = (from t in context.TPM_TASK 
     where t.USERID == user.UserId && t.STAGEID > 0 && t.STAGEID != 3 && t.TPM_PROJECTVERSION.STAGEID <= 10 
     orderby t.DUEDATE, t.PROJECTID 
     select t); 

Hinweis, wie wir t.USERID == user.UserId sind zu überprüfen.Dies erzeugt den gleichen Effekt wie user.TPM_TASK, aber jetzt wird das gesamte schwere Heben von der Datenbank und nicht im Speicher ausgeführt.

+0

Leider wird diese Idee nicht funktionieren. 'TPM_TASK' hat keine' USERID'-Eigenschaft. Benutzer beziehen sich auf Tasks über die Tabelle "TPM_USERTASK", die Sie nicht direkt abfragen können, da sie in einer Viele-zu-Viele-Beziehung verwendet wird. Vielleicht wäre es besser, eine Ansicht in der Datenbank zu erstellen oder so? –

+0

Sie können sicherlich die Abfrage auch gegen 'TPM_USERTASK' schreiben. Ich bin mir nicht sicher, wie genau dein Schema aufgebaut ist, aber etwas in der Art von 'where t.TPM_TASK.Any (y => y.USERID == t.USER)' oder benutze 'join', um das zu holen viel zu viel. Sie tun definitiv nicht mit Ansichten zu tun, um dies zu erreichen. –

+0

Okay, mit dieser Idee zu tun .. Ich werde Sie in ein paar Minuten wissen .. –

Verwandte Themen