UPDATE 3: Laut dieser Ankündigung wurde dies vom EF-Team in EF6 alpha 2 behoben.
UPDATE 2: Ich habe einen Vorschlag zur Behebung dieses Problems erstellt. Um dafür zu stimmen, klicken Sie hier .
Stellen Sie sich eine SQL-Datenbank mit einer sehr einfachen Tabelle vor.
CREATE TABLE Main (Id INT PRIMARY KEY)
Ich fülle die Tabelle mit 10.000 Datensätzen.
WITH Numbers AS
(
SELECT 1 AS Id
UNION ALL
SELECT Id + 1 AS Id FROM Numbers WHERE Id <= 10000
)
INSERT Main (Id)
SELECT Id FROM Numbers
OPTION (MAXRECURSION 0)
Ich erstelle ein EF-Modell für die Tabelle und führe die folgende Abfrage in LINQPad aus (ich verwende den Modus "C # -Anweisungen", damit LINQPad nicht automatisch einen Speicherauszug erstellt).
var rows =
Main
.ToArray();
Die Ausführungszeit beträgt ~ 0,07 Sekunden. Jetzt füge ich den Operator Enthält hinzu und führe die Abfrage erneut aus.
var ids = Main.Select(a => a.Id).ToArray();
var rows =
Main
.Where (a => ids.Contains(a.Id))
.ToArray();
Die Ausführungszeit für diesen Fall beträgt 20,14 Sekunden (288-mal langsamer)!
Zuerst vermutete ich, dass die Ausführung des für die Abfrage ausgegebenen T-SQL länger dauerte, und versuchte, es aus dem SQL-Bereich von LINQPad auszuschneiden und in SQL Server Management Studio einzufügen.
SET NOCOUNT ON
SET STATISTICS TIME ON
SELECT
[Extent1].[Id] AS [Id]
FROM [dbo].[Primary] AS [Extent1]
WHERE [Extent1].[Id] IN (1,2,3,4,5,6,7,8,...
Und das Ergebnis war
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 88 ms.
Als nächstes vermutete ich, dass LINQPad das Problem verursacht, aber die Leistung ist gleich, unabhängig davon, ob ich es in LINQPad oder in einer Konsolenanwendung ausführe.
Es scheint also, dass das Problem irgendwo im Entity Framework liegt.
Mache ich hier etwas falsch? Dies ist ein zeitkritischer Teil meines Codes. Kann ich also etwas tun, um die Leistung zu beschleunigen?
Ich verwende Entity Framework 4.1 und SQL Server 2008 R2.
UPDATE 1:
In der folgenden Diskussion gab es einige Fragen dazu, ob die Verzögerung aufgetreten ist, während EF die erste Abfrage erstellt oder die zurückerhaltenen Daten analysiert hat. Um dies zu testen, habe ich den folgenden Code ausgeführt:
var ids = Main.Select(a => a.Id).ToArray();
var rows =
(ObjectQuery<MainRow>)
Main
.Where (a => ids.Contains(a.Id));
var sql = rows.ToTraceString();
Dadurch wird EF gezwungen, die Abfrage zu generieren, ohne sie für die Datenbank auszuführen. Das Ergebnis war, dass für die Ausführung dieses Codes ~ 20 Sekunden erforderlich waren. Es scheint also, dass fast die gesamte Zeit für die Erstellung der ersten Abfrage benötigt wird.
CompiledQuery zur Rettung dann? Nicht so schnell ... CompiledQuery erfordert, dass die an die Abfrage übergebenen Parameter grundlegende Typen sind (int, string, float usw.). Es werden keine Arrays oder IEnumerable akzeptiert, daher kann ich es nicht für eine Liste von IDs verwenden.
var qry = Main.Where (a => ids.Contains(a.Id)); var rows = qry.ToArray();
zu sehen, welcher Teil der Abfrage die Zeit in Anspruch nimmt?