SQL-Abfragen vom Entity Framework loggen

In diesem Artikel sehen wir uns an, wie wir die SQL-Abfragen und Befehle vom Entity Framework loggen. Das ist manchmal notwendig, um Fehler bei der Erstellung oder Abfrage von Daten zu identifizieren.
Schnelleinstieg/Grundlagen
Das Entity Framework ist ein Objekt-Relationaler Mapper (ORM). Solche ORMs wandeln sozusagen eine Datenbank und SQL-Abfragen in Objekte und Methoden um.
Im einfachsten Falle gibt es eine Klasse Person
mit den Feldern Vorname, Nachname und ID. Ein ORM
wandelt diese Klasse in eine Tabelle mit 3 Spalten um. ORM-Frameworks stellen Möglichkeiten bereit,
sodass diese Daten beispielsweise durch MyDatabase.Person.SelectAll()
abgerufen werden können und
im Code eine Liste
Manchmal kommt es bei einer Abfrage zu Problemen. Hier bietet es sich manchmal an, den erstellten SQL-Code anzusehen und ggfs. selbst in einem Programm zur Datenbankverwaltung auszuführen.
Logging aktivieren
Entity Framework
Hier ist das Logging recht schnell implementiert. Dazu verwenden wir die Log-Methode im DbContext.
public class MyDbContext : DbContext {
public MyDbContext() {
this.Database.Log = (x) => Console.WriteLine(x);
}
public DbSet People { get; set; }
}
Hinweis: Das Entity Framework steht nur in .NET Framework Projekten zur Verfügung. NET Core oder Standard können nur Entity Framework Core verwenden.
Entity Framework Core
Das Logging wurde für .NET Core umgestellt. Sehr viele Klassen nutzen nun die Standardinterfaces von
Microsoft. In diesem Falle ist
es ILogger
. Wie
im oberen Beispiel erstellen wir einen DbContext. Allerdings konfigurieren wir das Logging in
der OnConfiguring
-Methode.
public class MyDbContext : DbContext {
public MyDbContext(){
}
public DbSet People { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder builder) {
builder.UseLoggerFactory(new LoggerFactory(new[] { new MyLoggerProvider() }));
// Weitere Konfigurationen..
}
}
Die Klasse LoggerFactory
wird durch das Entity Framework bereitgestellt. MyLoggerProvider
ist
unser Provider, welcher unsere Log-Klasse erstellt. Die beiden Klassen sehen folgendermaßen aus:
public class MyLoggerProvider : ILoggerProvider {
public void Dispose() {
}
public ILogger CreateLogger(string categoryName) {
return new ConsoleLogger();
}
}
public class ConsoleLogger : ILogger {
private LoggerExternalScopeProvider _scope = new LoggerExternalScopeProvider();
public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) {
formatter = formatter ?? ((o, ex) => $"State: {o} - Exception: {ex}");
Trace.WriteLine($" {logLevel}: {eventId} - {formatter(state, exception)}");
}
public bool IsEnabled(LogLevel logLevel) {
return true;
}
public IDisposable BeginScope(TState state) {
return \_scope?.Push(state) ?? NullScope.Instance;
}
}
Beim ConsoleLogger
habe ich mich an der Implementierung
vom Microsoft-ConsoleLogger
orientiert.
Die Methode IsEnabled
kann für jeden Typen (Debug, Info, Warnung) zurückgeben, ob geloggt werden
soll oder nicht. Die Scopes wurden mit .NET Core eingeführt. Es gibt eine Hierarchie, welche eine
dynamische Konfiguration ermöglicht.
Allerdings bin ich nicht in der Tiefe mit den Scopes vertraut und habe bisher „fertige“ Frameworks
wie Log4Net oder NLog verwendet.
Die Ausgabe
Die Ausgabe meines .NET Core Konsolenprojektes sieht so aus (Auszug):
Debug: Microsoft.EntityFrameworkCore.Database.Command.CommandExecuting - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END
FROM information_schema.tables
WHERE table_type = 'BASE TABLE' AND table_schema = 'MyDatabase'
Debug: Microsoft.EntityFrameworkCore.Database.Command.CommandExecuting - Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT `p`.`ID`, `p`.`FirstName`, `p`.`LastName`
FROM `People` AS `p`
Wie man sehen kann werden beispielsweise alle Tabellen der gewählten Datenbank ermittelt. Für das Beispiel habe ich ein Code-First Projekt erstellt. Dabei prüft das Entity Framework, ob alle notwendigen Tabellen vorhanden sind.
Später frage ich im Beispielcode alle Personen ab. Diese SQL-Abfrage ist schön im Log zu sehen.
Fazit
Wir haben uns angesehen, wie wir die Abfragen vom Entity Framework auf der Konsole ausgeben können. Dieses Ziel zu erreichen ist im „klassischen“ Entity Framework einfacher möglich als im Entity Framework Core.
Musstet ihr bereits Datenbankabfragen von ORMs nachschauen? Welche Werkzeuge benutzt ihr dafür?