Tipps und Tricks rund um .NET und Visual Studio

ADO.NET-Entity-Framework Ladeoptimierung durch Abfragen ohne Änderungsverfolgung
Kommentare

Im .NETversuim geht es heute um Ladeoptimierung durch Abfragen ohne Änderungsverfolgung beim ADO.NET-Entity-Framework.

Eine der zentralen Funktionen im ADO.NET-Entity-Framework ist die Änderungsverfolgung (Change Tracking) der geladenen Objekte. Ein SaveChange() auf dem Entity-Framework-Kontextobjekt (ObjectContext oder DbContext) speichert alle Änderungen seit dem Laden der Objekte oder dem letzten Speichern zurück in die Datenbank. Die Änderungsverfolgung im so genannten Objektzustandsmanager wirkt bis zur Attributebene, das heißt für eine Änderung an zwei Attributen in einem Objekt mit zehn Attributen gibt es nur zwei SET-Aktionen im Rahmen der SQL-UPDATE-Anweisung auf genau die beiden Spalten.

Die Änderungsverfolgung ist zwar sehr elegant, aber verbraucht auch Ressourcen und Rechenzeit. So dauert in einem konkreten Fall bei uns das reine Einlesen von allen 10 000 Datensätzen aus einer Tabelle mit Änderungsverfolgung 118 Millisekunden und ohne 74 Millisekunden. Ein anderer Fall bei uns macht es noch deutlicher: Das Filtern von 284 448 Datensätzen aus einer Tabelle mit 776 Millionen Datensätzen dauert mit Änderungsverfolgung 16,9 Sekunden, ohne nur 2,9 Sekunden (Abb.). Es lohnt sich also, die Änderungsverfolgung abzuschalten. Die Abschaltung der Änderungsverfolgung ist eine Option unter System.Data.Objects.MergeOption. Eine MergeOption kann man an folgenden Stellen angeben:

  • bei dem Entity Framework-Kontext für komplette Mengen:

db.Flug.MergeOption = MergeOption.NoTracking;

  • bei LINQ-Abfragen durch Typkonvertierung auf ObjectQuery:

var FlugQuery = (from x in db.Flug select x).OrderBy(f4 => f4.FlugNr).Skip(10);

(FlugQuery as ObjectQuery).MergeOption = MergeOption.NoTracking;

  • bei LINQ-Abfragen ab Entity-Framework 4.1 bei Verwendung von DbContext statt ObjectContext durch den NoTracking-Operator:

var FlugQuery = (from x in db.Flug.AsNoTracking() select x).OrderBy(f4 => f4.FlugNr).Skip(10);

  • bei SQL-Abfragen als Parameter bei ExecuteStoreQuery() oder Translate():

   var modell4 = new Modelle.EF6.WWWings6Entities();

   var q4 = modell4.ExecuteStoreQuery(„Select * from Flug where Abflugort = 

{0} and Zielort = {1}“, „Flug“, 

    System.Data.Objects.MergeOption.NoTracking, „Rom“, „Paris“);

  • beim Aufruf von gespeicherten Prozeduren muss man leider einen Umweg über ExecuteFunction() gehen. Beim typisierten Aufruf hat Microsoft leider keine Option für die Angabe der MergeOption vorgesehen:

ObjectParameter[] parameter = { new ObjectParameter(„von“, „Rom“), 

                                   new ObjectParameter(„nach“, „Paris“) };

var q5 = modell1.ExecuteFunction(„GetRoute“, MergeOption.NoTracking, parameter).ToList();

Die über NoTracking geladenen Objekte sind normale Instanzen der Entitätsklassen. Man kann sogar schreibend auf sie zugreifen. Aber das Entity-Framework ignoriert bei SaveChanges() alle diese Änderungen:

Console.WriteLine(q5.Count);

q5[0].FreiePlaetze–;

var e5 = modell2.SaveChanges();

Console.WriteLine(e5); // muss 0 ergeben

if (e5 > 0) Debugger.Break();

Dass das Entity-Framework die mit NoTracking geladenen Objekte nicht zurückspeichern kann, erkennt man daran, dass sie im Zustand detached sind, während normale Entity-Framework-Objekte nach dem Laden im Zustand unchanged sind. Diesen Umstand kann aber noch nachträglich ändern, indem man mit Attach() ein Objekt, das man ändern möchte, dem Kontext hinzufügt. Im Idealfall führt man vor der Änderung Attach() aus, dann merkt der Objektzustandsmanager des Entity-Frameworks sich genau, was sich am Objekt geändert hat (Listing 1). Hieraus entsteht folgender UPDATE-Befehl über die eine Spalte:

exec sp_executesql N’update [dbo].[Flug]

set [FreiePlaetze] = @0

    where ([FlugNr] = @1)

    ‚,N’@0 smallint,@1 int‘,@0=112,@1=145

Alternativ kann man Attach() auch nach der Änderung aufrufen und dem Objektzustandsmanager des Entity-Frameworks dann manuell mitteilen, dass das Objekt sich geändert hat (Listing 2). Allerdings weiß der Objektzustandsmanager dann nicht, welche Attribute im Objekt geändert wurden. Er wird also im UPDATE-Befehl alle Spalten neu setzen (Listing 3).

Listing 1

// Heilen des NoTracking-/Detached-Zustandes: Version A
modell1.Flug.Attach(f5);
f5.FreiePlaetze--;
Console.WriteLine(f5.EntityState);
var e6 = modell1.SaveChanges();
Console.WriteLine(e6); // muss 1 ergeben
if (e6 != 1) Debugger.Break();  

Listing 2

// Heilen des NoTracking-/Detached-Zustandes: Version B
f5.FreiePlaetze--;
modell1.Flug.Attach(f5);
modell1.ObjectStateManager.ChangeObjectState(f5, EntityState.Modified);
Console.WriteLine(f5.EntityState);
var e6 = modell1.SaveChanges();
Console.WriteLine(e6); // muss 1 ergeben
if (e6 != 1) Debugger.Break();  

Listing 3

exec sp_executesql N'update [dbo].[Flug]
set [Abflugort] = @0, [Zielort] = @1, [Datum] = @2, [NichtRaucherFlug] = @3, [Plaetze] = @4, [FreiePlaetze] = @5, [Pilot_PersonID] = null, [Ankunft] = null, [Memo] = null 
    where ([FlugNr] = @6)
    ',N'@0 nvarchar(20),@1 nvarchar(20),@2 datetime2(7),@3 bit,@4 smallint,@5 smallint,@6 int',@0=N'Rom',@1=N'Paris',@2='2011-08-03 07:16:59.8130000',@3=0,@4=0,@5=114,@6=145  
Diagramm zur Änderungsverfolgung von Datensätzen
Diagramm zur Änderungsverfolgung von Datensätzen

Dr. Holger Schwichtenberg (MVP) und FH-Prof. Manfred Steyer arbeiten bei IT-Visions als Softwarearchitekten, Berater und Trainer für .NET-Technologien. Dabei unterstützen Sie zahlreiche Unternehmen beim Einsatz von .NET und entwickeln selbst in größeren Projekten. Sie haben zahlreiche Fachbücher geschrieben und gehören seit vielen Jahren zu den Hauptsprechern auf der BASTA. Manfred Steyer ist zudem verantwortlich für den Fachbereich „Software Engineering“ der Studienrichtung „IT und Wirtschaftsinformatik“ an der FH CAMPUS 02 in Graz. Dr. Holger Schwichtenberg unterrichtet in Lehraufträgen an den Fachhochschulen Münster und Graz.
Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -