Entity Framework Abfragen optimieren
Wer den erzeugten SQL-Code des Entity Framework näher betrachtet hat, der kann sicherlich verstehen, warum es an einigen Stellen zu Performanceeinbussen kommen kann. In den Fachzeitschriften werden immer wieder Tipps und Tricks gezeigt, wie die Symptome behandelt werden können. Für die Version 4 wurden Verbesserungen angekündigt, auch am SQL-Code wurde gearbeitet. Die Beta2 habe ich bereits installiert und bei einem Projekt werde ich diese Neuerungen auch testen können.
Für die aktuelle Version hier ein einfaches Beispiel:
Für ein Objekt mit einer definierten ID sollen alle zugeordneten Mitarbeiter ermittelt werden.
C#
var result = (from e in this.Context.Employees.
Include("TargetSettings.TargetDesign.DesignRoot")
where e.TargetSettings.Any(
t => t.TargetDesign.DesignRoot.Id == id &&
t.TargetDesign.DesignRoot.BusinessUnit.Id == buId)
select new
{
Id = e.Id,
Name = e.Surname + " " + e.Givenname
}).ToList();
Dieses Statement liefert die Mitarbeiter zurück. Das Resultat ist in diesem Zusammenhang nicht von Bedeutung. Interessant ist der erzeugte SQL-Code, den EF1 erstellt hat.
T-SQL
SELECT
1 AS [C1],
[Extent1].[EM_AutoID] AS [EM_AutoID],
[Extent1].[EM_Surname] + N' ' + [Extent1].[EM_Givenname] AS [C2]
FROM [dbo].[tbEmployee] AS [Extent1]
WHERE EXISTS (SELECT
cast(1 as bit) AS [C1]
FROM (SELECT [Extent2].[TS_AutoID] AS [TS_AutoID], [Extent2].[TD_AutoID] AS [TD_AutoID1],
... Sehr viele Spalten entfernt ... [TD_User] AS [TD_User2]
FROM [dbo].[tbTargetSetting] AS [Extent2]
INNER JOIN [dbo].[tbTargetDesign] AS [Extent3] ON [Extent2].[TD_AutoID] = [Extent3].[TD_AutoID]
LEFT OUTER JOIN [dbo].[tbTargetDesign] AS [Extent4] ON [Extent2].[TD_AutoID] = [Extent4].[TD_AutoID]
WHERE 1 = [Extent3].[DR_AutoID] ) AS [Filter1]
INNER JOIN [dbo].[tbDesignRoot] AS [Extent5] ON [Filter1].[DR_AutoID2] = [Extent5].[DR_AutoID]
WHERE ([Extent1].[EM_AutoID] = [Filter1].[EM_AutoID]) AND (3 = [Extent5].[BU_AutoID])
)
Im SQL Management Studio habe ich eine Abfrage erstellt, so wie ich mir diese vorstellen würde, diese liefert das gleiche Ergebnis wie der Linq-Ausdruck zurück und hat folgenden Aufbau:
T-SQL SELECT EM_Surname + ' ' + EM_GivenName ,tbEmployee.EM_AutoID FROM tbEmployee WHERE EM_AUTOID IN ( SELECT EM_AUTOID FROM tbTargetSetting inner join tbTargetDesign on tbTargetSetting.TD_AutoID = tbTargetDesign.TD_AutoID inner join tbDesignRoot on tbDesignRoot.DR_AutoID = tbTargetDesign.DR_AutoID where tbTargetDesign.dr_autoID = 1 and BU_AutoID = 3 )
In meinem Szenario spielt es jedoch noch keine Bedeutung, es zeigt aber auf, dass Tricks wie zum Beispiel eine statische Verbindung bzw. Compiled Queries zwar eine Verbesserung mit sich bringen, aber nicht unbedingt zum gewünschten Erfolg führen. Es sollte also geprüft werden, inwieweit sich eine Abfrage optimieren lässt, bevor tiefer in die Trickkiste gegriffen und der Quellcode zu kreativ wird.
Wie sieht es jetzt in diesem Beispiel aus. Die Unterabfrage des vom Entity Framework erzeugten SQL-Codes enthält alle Spalten der benötigten Unterobjekte. Das ist sehr viel Ballast, der hier unnötig erzeugt wird. Dieser kommt zustande, weil ein Feld auf dem Objekt DesignRoot abgefragt wird.
In diesem Beispiel entferne ich die Abfrage nach der Id der Business Unit. Der erste Optimierungsversuch wäre dementsprechend zu prüfen, ob wirklich alle Parameter benötigt werden bzw. Abfragen auf die 3. Hierarchieebene zu vermeiden.
C#
var result = (from e in this.Context.Employees.
Include("TargetSettings.TargetDesign.DesignRoot")
where e.TargetSettings.Any(t => t.TargetDesign.DesignRoot.Id == id)
select new
{
Id = e.Id,
Name = e.Surname + " " + e.Givenname
}).ToList();
Der vom Entity Framework erzeugte SQL-Code sieht danach so aus:
T-SQL SELECT 1 AS [C1], [Extent1].[EM_AutoID] AS [EM_AutoID], [Extent1].[EM_Surname] + N' ' + [Extent1].[EM_Givenname] AS [C2] FROM [dbo].[tbEmployee] AS [Extent1] WHERE EXISTS (SELECT cast(1 as bit) AS [C1] FROM [dbo].[tbTargetSetting] AS [Extent2] INNER JOIN [dbo].[tbTargetDesign] AS [Extent3] ON [Extent2].[TD_AutoID] = [Extent3].[TD_AutoID] WHERE ([Extent1].[EM_AutoID] = [Extent2].[EM_AutoID]) AND (1 = [Extent3].[DR_AutoID]) )
Auf dem ersten Blick ist dieser Code schon etwas übersichtlicher. Aber wie sieht es mit den Abfragekosten in Relation zu meiner Abfrage aus?
- 0 Kommentar(e)





Mein Kommentar