Tipps und Tricks rund um .NET und Visual Studio

Mehr Durchblick dank Entity-Framework-Profiling
Keine Kommentare

Dr. Holger Schwichtenberg (MVP) teilt in der Kolumne „.NETversum“ sein Expertenwissen rund um .NET-Tools und WPF mit. In dieser Ausgabe geht es um Entity-Framework-Profiling.

Die in Entity Framework 6.0 eingeführte Log-Property in der Kontextbasisklasse DbContext erlaubt es, die zum Datenbank-managementsystem gesendeten Befehle auszugeben oder irgendwo zu speichern. Aber eine direkte Verbindung zum Programmcode ist nicht vorhanden. Wenn man wissen will, welche Zeile im Programmcode welchen SQL-Befehl ausgelöst hat, muss man schrittweise debuggen und dabei die Logausgabe jederzeit betrachten.

Entity-Framework-Profiling

Wenn man den Microsoft SQL Server Profiler (oder ein anderes datenbankspezifisches Profilingwerkzeug) benutzt, bekommt man den Zusammenhang zwischen SQL-Befehlen und Programmcode auch nicht.

Mehr Übersicht erhält man nur mit einem speziellen Profilingwerkzeug wie dem Entity Framework Profiler von Hibernating Rhinos oder LINQ Insight von Devart. Auch der ANTS Profiler von Red Gate hilft. Im Folgenden soll der Entity Framework Profiler von Hibernating Rhinos exemplarisch besprochen werden.

Der Entity Framework Profiler implementiert einen eigenen Entity Framework Provider, der sich zwischen das Entity Framework und den eigentlichen Datenbanktreiber hängt. So sendet das Entity Framework alle Befehle an den Treiber des Entity Framework Profilers, der die Befehle abgreift und dann an den eigentlichen Datenbanktreiber weiterleitet.

Damit das Tool überhaupt die Aktivitäten zwischen OR Mapper und Datenbanksystem mitschneiden kann, muss die zu überwachende Anwendung „instrumentiert“ werden. Dazu sind zwei Änderungen notwendig: Der Entwickler muss einen Verweis auf HibernatingRhinos.Profiler.Appender.dll erstellen, und zu Beginn muss die Zeile HibernatingRhinos.Profiler.Appender.EntityFramework.EntityFrameworkProfiler.Initialize() im Programmcode erscheinen.

Nun startet man die WPF-basierte Entity-Framework-Profiler-Benutzeroberfläche (EFProf.exe) vor der zu überwachenden Anwendung. Nach Start der zu überwachenden Anwendung sieht man dann im Entity Framework Profiler in der Liste links alle erzeugten Instanzen der Entity-Framework-Klasse ObjectContext (bzw. aller davon abgeleiteten Klassen). Leider sind die einzelnen Kontextinstanzen nicht benannt; die Benennung muss der Nutzer selbst in der Entity-Framework Profiler-Benutzeroberfläche vornehmen.

Jeweils zu einem Kontext sieht man die Anzahl der über den Kontext ausgeführten SQL-Befehle mit den jeweiligen Ausführungszeiten im DBMS selbst und die Gesamtzeit im RAM, also inklusive Materialisierung der Objekte. Abbildung 1 zeigt zum Beispiel sofort das Problem, dass viele Objektkontexte erzeugt werden, ohne dass überhaupt irgendein Befehl darüber zur Ausführung kommt.

Abb. 1: Viele Objektkontexte werden erzeugt

Im rechten Teil des Bildschirms findet man zum aktuell gewählten Kontext dann eine Liste der ausgeführten Befehle. Der STACK TRACE offenbart, welche Methode einen einzelnen SQL-Befehl ausgelöst hat. Sehr nett ist, dass ein Doppelklick auf einen Eintrag im STACK TRACE direkt zu der passenden Codestelle im geöffneten Visual Studio führt. Damit findet man dann sehr schnell den LINQ-to-Entities- oder Entity-SQL-Befehl, der den SQL-Befehl ausgelöst hat.

Unter DETAILS kann man den kompletten SQL-Befehl mit Parametern und den zugehörigen Ausführungsplan grafisch sehen (Abb. 2).

Abb. 2: SQL-Befehl unter Details

Der Entity Framework Profiler setzt aber nicht voraus, dass die Anwendung im Visual Studio Debugger läuft. Die Aufzeichnung funktioniert auch, wenn die Anwendung direkt gestartet wird – sogar wenn sie im Releasemodus kompiliert wurde. Entscheidend ist nur die eine Zeile Instrumentierungscode. Man kann die Anwendung also so gestalten, dass der Instrumentierungscode bei Bedarf aufgerufen wird, z. B. gesteuert über eine Konfigurationsdatei.

Besondere Beachtung verdienen die grauen Kreise (Vorschläge) und roten Kreise (Warnungen) in der Liste der ausgeführten Befehle. Hier will der Entity Framework Profiler ein potenzielles Problem entdeckt haben. In der ersten Abbildung ist das – so wie er es nennt – ein SELECT N+1-Problem. Eine Vielzahl gleichartiger SQL-Befehle, die nacheinander ausgeführt werden, deutet darauf hin, dass hier fehlerhafterweise Lazy Loading zum Einsatz kommt. Man sollte Eager Loading in Erwägung ziehen.

Ein anderes Problem, das der Entity Framework Profiler sehr gut aufzeigt, ist die wirklich nicht empfohlene Verwendung eines Entity-Framework-Kontextobjekts in verschiedenen Threads. Andere Hinweise gibt es, wenn eine Abfrage viele Joins verwendet, mit einem Platzhalter (like „%xy“) beginnt, viele Datensätze zurückliefert und keine Topanweisung enthält (Unbounded Result Sets). Gerade über den letzten Punkt kann man durchaus streiten. Intention dieses Vorschlags ist, dass ein Entwickler nicht Gefahr laufen sollte, dass er viel mehr Datensätze abfragt, als er wirklich erwartet und braucht. Aber in der Praxis lässt sich (außer bei Anwendungen, die Datensätze explizit mit Blätternfunktion anzeigen) oft keine Obergrenze festlegen, die dauerhaft gilt. Eine Warnung gibt es auch, wenn viele INSERT-, UPDATE- und DELETE-Befehle ausgeführt werden und man prüfen sollte, ob dies nicht durch eine Massenoperation abbildbar ist.

Sehr hilfreich sind die Analysefunktionen in der Registerkarte <span style=“font-variant:small-caps;“>ANALYSIS</spam>. Hier gibt es Auswertungen, die zeigen

  • welche Methoden im Programmcode welche SQL-Befehle ausgelöst haben (Queries by Method) (Abb. 2)
  • wie viele verschiedene Befehle es gab (trotz des Namens Unique Queries erscheinen hier auch INSERT-, UPDATE- und DELETE-Befehle!)
  • welche Befehle am längsten gedauert haben (Expensive Queries)

Eine sehr interessante Funktion verbirgt sich auch im Menü FILE/EXPORT TO FILE. Hierdurch entsteht eine mit JavaScript angereicherte HTML-Seite, in der man eine ähnliche Oberfläche wie im Entity Framework Profiler sieht. Man kann alle Kontexte und SQL-Befehle betrachten und die Analyseergebnisse aufrufen. Es fehlt aber der Stack Trace und die Warnungen.

Die normale Speicherfunktion erzeugt eine Binärdatei mit der Dateinamenserweiterung .efprof. Eine solche Datei kann auch der zu überwachende Programmcode direkt erzeugen, indem man im Startbefehl statt Initialize() die Methode InitializeOfflineProfiling(Dateiname.efprof) aufruft. Dann muss die Entity-Framework-Profiler-Benutzeroberfläche nicht laufen, während die Anwendung läuft. Ein Profiling ist also auch auf Zielsystemen problemlos möglich.

Im Sinne von Continous Integration kann man den Entity Framework Profiler auch von der Kommandozeile ausführen. Man braucht aber pro Rechner eine Lizenz.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Hinterlasse einen Kommentar

Hinterlasse den ersten Kommentar!

avatar
400
  Subscribe  
Benachrichtige mich zu:
X
- Gib Deinen Standort ein -
- or -