Datenpunkte

Profilerstellung für Datenbankaktivitäten im Entity Framework

Julie Lerman

Beispielcode herunterladen

In der Datenpunkte-Kolumne des letzten Monats (msdn.microsoft.com/magazine/gg309181) habe ich über die Profilerstellung der Leistung des Entity Framework vor dem Hintergrund von SQL Azure geschrieben. In diesem Monat betrachte ich eine andere Art der Profilerstellung – die Profilerstellung für Abfragen – um zu sehen, welche Abfragen und Befehle auf der Datenbank ausgeführt werden, um auf Abfragen und andere Datenzugriffsaktivitäten im Entity Framework zu antworten.

Eine der wichtigsten Funktionen des Entity Framework ist die Befehlsgenerierung für die Ausführung von Datenbankabfragen sowie für Einfüge-, Update- und Löschvorgänge. Dies stellt für zahlreiche Entwickler einen großen Vorteil dar, wenn es auch stets eine Diskussion über die Qualität von durch Tools für die objektrelationale Zuordnung (Object-Relational Mapping, ORM) generiertem SQL im Vergleich zu manuell erstelltem SQL geben wird (ich werde diese Diskussion in diesem Artikel nicht behandeln). Das auf diese Art generierte SQL ist überwiegend ziemlich gut, besonders, wenn man berücksichtigt, dass es dynamisch auf generische Weise konstruiert werden muss, unabhängig davon, wie kreativ Sie mit Ihren LINQ to Entities- oder Entity-SQL-Abfrageausdrücken sind.

Obwohl der Verbesserung der Befehlsgenerierung im Entity Framework 4 große Aufmerksamkeit gewidmet wurde, ist es weiterhin wichtig, über die Vorgänge in Ihrer Datenbank informiert zu bleiben. Die Qualität der generierten Speicherabfrage ist nur ein Teil des Problems. Sie erstellen möglicherweise Code, der erweiterte Datenbankausführungszeiten oder eine ungewöhnliche Anzahl von Trips für die Datenbank erstellt. Dies sind wichtige Themen, die Sie bei der Profilerstellung für Ihre Anwendung berücksichtigen sollten.

In den ersten Jahren von Entity Framework gab es keine anderen Tools als die Profilerstellungstools der Datenbanken, wie z. B. SQL Profiler, der zwar zahlreiche Informationen bereitstellt, jedoch einen hohen Konfigurations- und Miningaufwand erfordert, wenn Sie die Ergebnisse benutzerfreundlich anzeigen möchten. Im Folgenden finden Sie mehrere Optionen für die Abfrageprofilerstellung für das Entity Framework außerhalb der Profilerstellungstools der Datenbanken.

Die ObjectContext.ToTraceString-Methode von Entity Framework

Die Entity Framework API (3.5 und 4) stellt eine einzelne Methode für die Prüfung von Abfragen zur Laufzeit bereit, ToTraceString. Diese ist nützlich, stellt jedoch nur für einen Teil der Aufrufe für die Datenbank Informationen bereit. ToTraceString ist eine Methode von ObjectQuery. Wenn Sie eine LINQ to Entities-Abfrage erstellen, müssen Sie daher die Abfrage zunächst an ein ObjectQuery leiten, bevor Sie ToTraceString aufrufen können. Im Folgenden finden Sie ein Beispiel:

var query = from c in context.Customers where c.CustomerID == 3 select c;
var objectQuery=query as System.Data.Objects.ObjectQuery;
Console.WriteLine(objectQuery.ToTraceString());

Dies gibt die folgende Zeichenfolge aus:

SELECT
[Extent1].[CustomerID] AS [CustomerID],
[Extent1].[Title] AS [Title],
[Extent1].[FirstName] AS [FirstName],
[Extent1].[MiddleName] AS [MiddleName],
[Extent1].[LastName] AS [LastName],
[Extent1].[Suffix] AS [Suffix],
[Extent1].[CompanyName] AS [CompanyName],
[Extent1].[SalesPerson] AS [SalesPerson],
[Extent1].[EmailAddress] AS [EmailAddress],
[Extent1].[Phone] AS [Phone],
[Extent1].[ModifiedDate] AS [ModifiedDate],
[Extent1].[TimeStamp] AS [TimeStamp]
FROM [SalesLT].[Customer] AS [Extent1]
WHERE 3 = [Extent1].[CustomerID]

Beachten Sie, dass der Beispielcode die Abfrage nicht ausführt. ToTraceString führt die Abfrage ebenfalls nicht aus. Daher wird die Abfrage (nach der Transformation zu einer Speicherabfrage) durch das Entity Framework mit Unterstützung durch den Datenbankanbieter, System.Data.SqlClient, verarbeitet. Die Abfrage muss nicht auf der Datenbank ausgeführt werden.

Sie können ToTraceString nur mit ausdrücklich definierten Abfragen verwenden. Daher können Sie die Methode nicht für die Anzeige der Ausführung von Abfragen als Ergebnis eines aufgeschobenen Ladevorgangs mittels der Load-Methode oder Lazy Loading verwenden. Sie können sie auch nicht für die Prüfung von Aktivitäten wie Einfüge-, Update- oder Löschvorgänge oder der Ausführung einer gespeicherten Prozedur verwenden.

Schließlich ist es wichtig zu beachten, dass Sie die ToTraceString-Ergebnisse nicht einfach z. B. mittels der Erstellung eines Debuggingvisualisierungstools in einen Debugvorgang einfügen können. Dafür müsste ObjectQuery serialisierbar sein, was es nicht ist.

Profilerstellung mit Visual Studio 2010 IntelliTrace

IntelliTrace ist in Visual Studio 2010 Ultimate, nicht jedoch in Versionen mit geringerem Funktionsumfang verfügbar. IntelliTrace erfasst die Datenbankaktivität, einschließlich der Aktivität, die durch das Entity Framework ausgelöst wird. Es zeigt jedoch nicht die Parameterwerte an, die mit dem Befehl gesendet wurde.

Im Folgenden finden Sie Code, der die folgenden Aufgaben ausführt:

  1. Ausführung einer Abfrage von 10 Kunden
  2. Lazy Loading der Bestellungen für den ersten zurückgegebenen Kunden
  3. Deaktivierung des Lazy Loading
  4. Ausdrückliches Laden der Bestellungen für den zweiten zurückgegebenen Kunden
  5. Modifizierung eines Kunden
  6. Senden der Änderungen an die Datenbank mittels der SaveChanges-Methode
  7. Ausführung einer Funktion, die ich einer gespeicherten Prozedur in einem Entity Data Model zugeordnet habe
var query = from c in context.Customers select c;
var custList = query.Take(10).ToList();

Customer custFirst = custList[0];
int orderCount = custFirst.Orders.Count; 

context.ContextOptions.LazyLoadingEnabled=false;
Customer custSecond = custList[1];
custSecond.Orders.Load(); 

custSecond.ModifiedDate = DateTime.Now;
context.SaveChanges(); 

ObjectResult<PartialOrderDetails> orders= 
  context.GetCustomerOrdersForId(custList[2].CustomerID);

Bei Ausführung erzwingt dieser Code die Ausführung von drei SELECT-Anweisungen, einer UPDATE-Anweisung und anschließend eines Ausführungsbefehls für die gespeicherte Prozedur in der Datenbank.

Bei Betrachtung des IntelliTrace-Screenshots in Abbildung 1 können Sie alle fünf Befehle erkennen.

image: A Series of Database Commands Displayed in the Visual Studio IntelliTrace Display

Abbildung 1 Eine Reihe von Datenbankbefehlen, die in der Visual Studio IntelliTrace-Anzeige angezeigt werden

Die Erweiterung eines dieser Elemente, wie in Abbildung 2 gezeigt, zeigt, dass die Befehle zwar vorhanden sind, nicht jedoch die Parameter.

image: A Detailed Select Statement Collected by the Visual Studio 2010 IntelliTrace Feature

Abbildung 2 Eine detaillierte Select-Anweisung, die durch die Visual Studio 2010 IntelliTrace-Funktion erfasst wurde

Wenn Sie daher diese Datenbankaktivität einschließlich der Parameter anzeigen möchten, müssen Sie ein externes Tool für die Profilerstellung verwenden.

Der EFTracingProvider in der MSDN-Codegalerie

Jarek Kowalski schrieb den EFTracingProvider, als er Mitglied des Entity Framework-Teams von Microsoft war. Es gibt eine Version für Microsoft .NET Framework 3.5 und eine Version für Microsoft .NET Framework 4.

Um EFTracingProvider zu verwenden, müssen Sie einen Wrapper für die ObjectContext-Klasse „AWEntities“ erstellen und diesen anstelle von „AWEntities“ verwenden. Diese erweiterte Klasse stellt Methoden für die Ablaufverfolgung wie Log bereit, die Sie für die Protokollierung von Kontextaktivitäten verwenden können. Es gibt ein Beispiel für den erforderlichen Klassenwrapper, der im EFTracingProvider-Download enthalten ist. Sie können den Code auch im Download für diesen Artikel finden (code.msdn.microsoft.com/mag201012DataPoints). Zusätzlich müssen Sie der config-Datei der Anwendung zwei DbProviderFactories-Einstellungen hinzufügen. Wenn dies alles erfolgt ist, können Sie den erweiterten Kontext instantiieren und mit der Protokollierung beginnen.

Im Folgenden finden Sie ein Beispiel, mit dem eine Textdatei für die Erfassung der Protokollereignisse erstellt wird. Anschließend wird die TracingProvider.Log-Methode für die Protokollierung all dieser Aktivitäten verwendet:

using (TextWriter logFile = File.CreateText("sqllogfile.txt"))
{
  using (var context = new ExtendedAWEntities())
  {
    context.Log = logFile;
    var query = from c in context.Customers select c;
    var custList = query.Take(10).ToList();
  }
  Console.WriteLine(File.ReadAllText("sqllogfile.txt"));
}

Ich führte unter Verwendung des TracingProvider-Wrappers und der ExtendedAWEntities-Kontextklasse den gleichen Code wie im vorherigen IntelliTrace-Beispiel aus.

Alle fünf Datenbankbefehle wurden protokolliert, und jeder Befehl wurde mit den relevanten Parametern protokolliert.

Abbildung 3 zeigt als Beispiel den als Ergebnis des Lazy Loading gesendeten Befehl, in dem der Wert des EntityKeyValue1-Parameters nach der Auflistung des Befehls angegeben wird.

Abbildung 3 Ein mittels EFTracingProvider erfasster Befehl

    SELECT
    [Extent1].[SalesOrderID] AS [SalesOrderID],
    [Extent1].[OrderDate] AS [OrderDate],
    [Extent1].[DueDate] AS [DueDate],
    [Extent1].[OnlineOrderFlag] AS [OnlineOrderFlag],
    [Extent1].[SalesOrderNumber] AS [SalesOrderNumber],
    [Extent1].[PurchaseOrderNumber] AS [PurchaseOrderNumber],
    [Extent1].[AccountNumber] AS [AccountNumber],
    [Extent1].[CustomerID] AS [CustomerID],
    [Extent1].[BillToAddressID] AS [BillToAddressID],
    [Extent1].[CreditCardApprovalCode] AS [CreditCardApprovalCode],
    [Extent1].[SubTotal] AS [SubTotal],
    [Extent1].[Comment] AS [Comment],
    [Extent1].[ModifiedDate] AS [ModifiedDate],
    [Extent1].[ShipDate] AS [ShipDate]
    FROM [SalesLT].[SalesOrderHeader] AS [Extent1]
    WHERE [Extent1].[CustomerID] = @EntityKeyValue1
    -- EntityKeyValue1 (dbtype=Int32, size=0, direction=Input) = 1

Der EFTracingProvider kann einfach implementiert werden und stellt Ihnen alle von Ihrem Entity Framework-Code generierten Datenbankbefehle im Textformat zur Verfügung. Sie können die Ablaufverfolgungsereignisse auch abonnieren: CommandExecuting, CommandFinished und CommandFailed für den Kontext. Damit erhalten Sie Zugriff auf DbCommand unmittelbar vor und nach dessen Ausführung, sodass Sie zusätzliche Details analysieren oder protokollieren können.

Sie können den EFTracingProvider zusammen mit dem EFCachingProvider und einer Beispiellösung (EFProviderWrapperDemo), in der alle diese Funktionen untersucht werden, kostenlos von der MSDN-Codegalerie (code.msdn.microsoft.com/EFProviderWrappers) herunterladen.

Tools für die Profilerstellung von Drittanbietern

Möglicherweise möchten Sie mehr als den reinen Text der EFTracingProvider-Protokolldatei. Sie können den Code in diesen Protokolldateien nutzen und von diesen lernen, oder Sie können zwei Tools verwenden, die dies bereits für Sie erledigt haben. Es gibt zwei Tools von Drittparteien für die Profilerstellung von Entity Framework-Abfragen: Hibernating Rhinos Entity Framework Profiler und Huagati Query Profiler.

Zusätzlich zeigt LINQPad, dessen Schwerpunkt auf Tests von Abfrageausdrücken außerhalb Ihrer Anwendung liegt, SQL für die von Ihnen ausgeführten Ausdrücke an. Obwohl es sich hierbei um ein unverzichtbares Tool für alle Entwickler handelt, die LINQ für eine große Zahl von Anbietern erstellen, ermöglicht es Ihnen nicht die Profilerstellung für die Abfragen, die von Ihrer Anwendung generiert werden. Daher werde ich dieses Tool in dieser Kolumne nicht weiter behandeln.

Entity Framework Profiler (EF Prof) ist Teil der Hibernating Rhinos UberProf-Familie von Tools für die Profilerstellung (hibernatingrhinos.com/products/UberProf). Es gibt auch Tools für die Profilerstellung für nHibernate, Hibernate und LINQ to SQL. Ein fünftes Tool, LLBLGen Pro, befand sich in der Betaphase, als dieser Artikel verfasst wurde. EF Prof verbindet das vorhandene geistige Eigentum der übrigen UberProf-Tools mit einigen Ideen, die aus EFTracingProvider stammen. Am einfachsten ist es, wenn Sie Ihrer Anwendung eine einzelne Codezeile hinzufügen, damit diese mit dem EF Prof-Modul interagieren kann und die Ergebnisse in der EF Prof-Clientanwendung berichtet werden.

HibernatingRhinos.Profiler.  
    Appender.EntityFramework.
    EntityFrameworkProfiler.Initialize

Die Datenbankaktivität wird nach ObjectContext-Instanz gruppiert. In Abbildung 4, können Sie erkennen, dass zwei ObjectContext-Instanzen angezeigt werden. Daher habe ich den Beispielcode zweimal ausgeführt. 

image: The EF Prof Query Profiler UI

Abbildung 4 Die Benutzeroberfläche des EF Prof-Tools für die Profilerstellung von Abfragen

Sie können rechts in Abbildung 4 auch sehen, dass es eine Vorschau der einzelnen Datenbankaufrufe für die ausgewählte Kontextinstanz gibt. Scheinbar wird nach dem UPDATE-Befehl eine zusätzliche SELECT-Anweisung aufgerufen. Diese ist jedoch Teil des Befehls, der mit SaveChanges gesendet wird, da das Entity Framework so sicherstellt, dass das aktualisierte TimeStamp-Feld der Customer-Zeile zur Kundeninstanz zurückgegeben wird.

Wenn Sie in der Benutzeroberfläche eine SQL-Anweisung markieren, wird das vollständige SQL unten auf dem Bildschirm angezeigt, zusammen mit einem Hinweis auf die Tatsache, dass der Wert – in diesem Fall „5“ – als Parameter @EntityKeyValue1 übergeben wurde.

EF Prof ermöglicht Ihnen auch die Anzeige der Ergebniszeilen aus der Abfrage und sogar des Datenbankabfrageplans. Die Registerkarte für die Stackablaufverfolgung, wie in Abbildung 5 gezeigt, ermöglicht Ihnen anzuzeigen, warum die Anwendung einen bestimmten Befehl ausgeführt hat, und außerdem den direkten Wechsel zu dieser Codezeile in Visual Studio.

image: The EF Prof Stack Trace Lets You Jump to the Code that Executed the Selected Database Command

Abbildung 5 Die EF Prof-Stackablaufverfolgung ermöglicht Ihnen den Wechsel zu dem Code, der den ausgewählten Datenbankbefehl ausgeführt hat

EF Prof kann alle Entity Framework-Aktivitäten einer Anwendung erfassen und diese in einer einfach zu navigierenden Benutzeroberfläche anzeigen, die über einige interessante Funktionen verfügt, wie z. B. die Anzeige des Abfrageplans, und mit dem ausführenden Code verknüpft ist. Eine Standardlizenz von EF Prof kostet US$305, mit Nachlässen für den Erwerb mehrerer Lizenzen und eines Abonnementplans. EF Prof funktioniert mit allen Entity Framework-Datenanbietern und ist daher nicht auf SQL Server beschränkt. Das Tool funktioniert mit den .NET Framework-Versionen 3.5 und 4.

Huagati Query Profiler hieß ursprünglich L2S Profiler und wurde im November aktualisiert, um Unterstützung für das Entity Framework 4 bereitzustellen. Sie können das Tool auch für die Profilerstellung für LINQ to SQL und LLBLGen Pro verwenden. Zurzeit funktioniert es jedoch nur mit SQL Server.

Die Implementierung des Profilerstellungstools für Abfragen beinhaltet Verweise auf die Assembly des Profilerstellungstools (Huagati.EFProfiler.dll) in Ihrer Anwendung. Außerdem müssen Sie zwei neue Konstruktoren und Ihrer ObjectContext-Klasse in einer Teilklasse zusätzliche Logik hinzufügen. Abbildung 6 zeigt die Teilklasse, die ich für meine AWEntities-Klasse erstellt habe.

Abbildung 6 Die Teilklasse für die Verwendung mit Huagati Query Profiler

string profilerOutput =
      System.IO.Path.Combine(System.Environment.GetFolderPath(
        Environment.SpecialFolder.Personal),
      @"EFProfiler\Samples");
    _profiler=new HuagatiEFProfiler.EFProfiler(this, profilerOutput, null, 
      HuagatiEFProfiler.ExecutionPlanMode.Actual, false);
   _profiler.LogError += EFProfiler_LogError;
  }
}

Die EFProfiler.GetConnection-Methode stellt eine Verbindung mit der Datenbank her, um deren Aktivität zu verfolgen. Weitere Informationen zu den einzelnen Einstellungen, die Sie bei der Instantiierung von EFProfiler verwenden können, finden Sie auf der Website von Huagati.

Das Tool für die Profilerstellung sammelt die Daten und stellt sie in einer Datei im angegebenen Ordner bereit. Sie können diese Datei im Protokoll-Explorer des Tools öffnen, wie in Abbildung 7 gezeigt.

image: The Huagati Entity Framework Query Profiler UI

Abbildung 7 Die Benutzeroberfläche des Huagati-Tools für die Profilerstellung von Entity Framework-Abfragen

Wie Sie in Abbildung 7 sehen können, werden alle fünf Datenbankanfragen gesammelt. Der Update-Befehl wird mit dessen SELECT-Befehl verbunden, um TimeStamp zurückzugeben. Der Befehl wird genau auf diese Weise an die Datenbank gesendet.

Der in Abbildung 7 gezeigte Protokoll-Explorer zeigt die relevanten Zeilen aus den SQL Server SQL Profiler-Daten an. Wie im Fall von EF Prof können Sie die Abfrage mit ihren Parametern anzeigen, von der Stackansicht zum relevanten Code in Ihrer Anwendung wechseln, den Ausführungsplan anzeigen sowie einige Statistiken zu den Abfragen erhalten.

Die Datenbankaktivität für die einzelnen Kontextinstanzen wird in einer eigenen Protokolldatei gespeichert, sodass der Protokoll-Explorer nur jeweils einen Befehlssatz anzeigt. Mithilfe der Einstellungen können Sie Benachrichtigungen farblich markieren, um ungewöhnliche Aktivitäten hervorzuheben, wie z. B. spürbar oder sogar alarmierend lange Ausführungszeiten.

Die Query Profiler-Benutzeroberfläche ist nicht so elegant wie die EF Prof-Benutzeroberfläche, und Sie müssen etwas mehr in den Code investieren (indem Sie jedem ObjectContext-Objekt Ihrer Anwendung Logik hinzufügen). Die Komponenten sind jedoch verteilbar. Das bedeutet, dass Sie Profilerstellungsinformationen für Anwendungen sammeln können, die in den Umgebungen Ihrer Kunden ausgeführt werden. Außerdem verfügt das Tool über weniger Analyseoptionen als EF Prof. Der Preis von US$20 für die Standard-Version und US$40 für die Professional-Version (in der alle drei Profilerstellungstools enthalten sind) gleicht diesen Unterschied für viele Entwickler wahrscheinlich wieder aus. Denken Sie daran, dass sich das Huagati-Profilerstellungstool für das Entity Framework noch in der Betaphase befand, als ich es untersuchte, und dass es nur mit SQL Server funktioniert, während EF Prof mit allen erhältlichen ADO.NET-Datenanbietern funktioniert, die das Entity Framework unterstützen.

 Eine Einführung in die Unterstützung von Huagati für das Entity Framework finden Sie unter tinyurl.com/26cfful. Am Ende dieses Blogs finden Sie einen Link für den Download der Betaversion (1.31).

Auswahl des richtigen Tools

Ich bin davon überzeugt, dass es darauf ankommt, das richtige Tool zu verwenden, und das es keinen Sinn macht, Funktionen aus Visual Studio 2010 herauspressen zu wollen, wenn es andere gute Tools für spezielle Aufgaben gibt. In dieser Kolumne habe ich Ihnen eine Reihe von Tools gezeigt, die in die Entity Framework-APIs und in Visual Studio 2010 integriert sind: eine Erweiterung, die Ihnen reine Textdaten bereitstellt, und zwei Tools von Drittparteien, die nicht nur die Daten sammeln, sondern diese auch präsentieren. Für welche Möglichkeit Sie sich auch immer entscheiden – Sie sollten Ihre Datenbank nicht für selbstverständlich halten, wenn Sie Ihre Anwendung profilieren, auch dann nicht, wenn Sie einfach nur SQL Profiler verwenden.

Julie Lerman ist Microsoft MVP, .NET-Mentor und Unternehmensberaterin und lebt in den Bergen von Vermont. Sie hält bei User Groups und Konferenzen in der ganzen Welt Vorträge zum Thema Datenzugriff und anderen Microsoft .NET-Themen. Julie Lerman führt einen Blog unter thedatafarm.com/blog und ist Autorin des ausgezeichnet bewerteten Buchs „Programming Entity Framework“ (Programmieren des Entity Framework) (O’Reilly Media 2010). Folgen Sie ihr auf twitter.com: julielerman.

Unser Dank gilt dem folgenden technischen Experten für die Durchsicht dieses Artikels: Jarek Kowalski