2010-08-17 16 views
10

Ich habe eine Eltern-Kind-Tabelle-Beziehung. In einem Repository, mache ich das:EF dauert für immer, um diese Abfrage zu generieren

return (from p in _ctx.Parents 
.Include("Children") 
select p).AsQueryable<Parent>(); 

Dann in einem Filter, möchte ich durch eine Liste der untergeordneten ids die Eltern filtern:

IQueryable<Parent> qry; // from above 
List<int> ids; // huge list (8500) 
var filtered = 
from p in qry.Where(p => p.Children.Any(c => ids.Contains(c.ChildId))) select s; 

Meine Liste von IDs ist riesig. Dies erzeugt eine einfache SQL-Anweisung, die eine riesige Liste von IDs "in (1,2,3 ...)" hat, aber es braucht keine nennenswerte Zeit, um von selbst zu laufen. EF benötigt jedoch ungefähr eine volle Minute, um die Aussage zu erzeugen. Ich habe dies bewiesen, indem ich einen Haltepunkt setzte und anrief:

((ObjectQuery<Parent>)filtered).ToTraceString(); 

Das dauert die ganze Zeit. Ist das Problem in meiner letzten linq-Anweisung? Ich kenne keinen anderen Weg, um das Äquivalent von Child.ChildId in (ids) zu machen. Und selbst wenn meine Linq Aussage schlecht ist, wie soll das in der Welt so lange dauern?

+0

Veröffentlichen Sie Ihr Schema und die generierte SQL, erhalten Sie möglicherweise weitere Hilfe. –

+0

Das Schema scheint keine Rolle zu spielen, ebenso wenig wie die Eltern-Kind-Beziehung. Selbes Problem nur aus einer einzelnen Tabelle/Entity mit großer Liste auswählen. – dudeNumber4

+0

Für alle anderen, die auf das Problem stoßen; Von allem, was ich sagen kann, gibt es keine Lösung mit EF4. Sie müssen auf gespeicherte Prozeduren zurückgreifen. Weitere Informationen: http://social.msdn.microsoft.com/Forums/en-US/adodottertentityframework/thread/d629c798-db45-4a04-9813-a3b565d87c83 – dudeNumber4

Antwort

2

Schreiben Sie Ihre Abfrage in Lambda-Syntax und es wird die Zeit um bis zu 3 Sekunden (oder zumindest für mein EF-Projekt).

return _ctx.Parents.Include("Children").AsQueryable<Parent>(); 

und

IQueryable<Parent> qry; // from above 
List<int> ids; // huge list (8500) 
var filtered = qry.Where(p => p.Children.Any(c => ids.Contains(c.ChildId))); 
+0

'Select (s => s)' tut nichts. Beende es nach dem Wo. – StriplingWarrior

+0

Richtig ... Danke! –

+0

Das machte in meinem Fall keinen Unterschied. – dudeNumber4

4

Leider Erstellen von Abfragen in Linq to Entities ist ein ziemlich schwerer Schlag, aber ich habe es in der Regel die Zeit gefunden, aufgrund der Fähigkeit spart Anfragen von ihren Teilstücken zu bauen vor tatsächlich trifft die Datenbank.

Es ist wahrscheinlich, dass die Art, wie sie die Contains-Methode implementieren, einen Algorithmus verwendet, der davon ausgeht, dass Contains im Allgemeinen für einen relativ kleinen Datensatz verwendet wird. Laut meinen Tests beginnt die Zeit pro ID in der Liste um 8000 zu explodieren.

So könnte es helfen, Ihre Anfrage in Stücke zu brechen. Gruppieren Sie sie in Gruppen von 1000 oder weniger und verketten Sie eine Reihe von Ausdrücken von Where.

var idGroups = ids.GroupBy(i => i/1000); 
var q = Parents.Include("Children").AsQueryable(); 
var newQ = idGroups.Aggregate(q, 
    (s, g) => s.Concat(
        q.Where(w => w.Children.Any(wi => g.Contains(wi.ChildId))))); 

Dies beschleunigt die Dinge deutlich, aber es ist vielleicht nicht für Ihre Zwecke deutlich genug sein, wobei in diesem Fall werden Sie auf eine gespeicherte Prozedur zurückgreifen müssen. Leider passt dieser spezielle Anwendungsfall einfach nicht in die "Box" des erwarteten Entity Framework-Verhaltens. Wenn Ihre Liste von IDs als Abfrage aus demselben Entitätskontext beginnen könnte, hätte Entity Framework gut funktioniert.

+0

Upvoted, aber nicht als Antwort markiert. Die Verwendung von gespeicherten Prozeduren ist die Antwort, aber ich werde die Lösung für fehlerhafte Abfragen auf keinen Fall verwenden. Ich werde nur die gebrochene EF-Lösung verwenden ... – dudeNumber4

+0

Ich würde Sie ermutigen, entweder ein Kopfgeld zu starten oder eine Antwort zu markieren. Selbst wenn Sie Ihre eigene Stored-Proc-basierte Antwort auf Ihre Frage schreiben, wird in der FAQ klargestellt, dass dies akzeptabel ist.Natürlich würde ich auch darauf hinweisen, dass ich nicht nur die gestellte Frage beantwortet habe, sondern auch die Lösung geliefert habe, die sich so anhört, als ob sie sie verwenden würde. – StriplingWarrior

+0

Dies ist wahrscheinlich die beste Antwort, die Sie bekommen werden. Wenn jemand nicht mit dem Framework fertig ist, ist dies die Antwort. –