Prestandaöverväganden (Entity Framework)
Det här avsnittet beskriver prestandaegenskaperna för ADO.NET Entity Framework och innehåller några överväganden för att förbättra prestandan för Entity Framework-program.
Faser av frågekörning
För att bättre förstå prestanda för frågor i Entity Framework är det bra att förstå de åtgärder som inträffar när en fråga körs mot en konceptuell modell och returnerar data som objekt. I följande tabell beskrivs den här serien med åtgärder.
Åtgärd | Relativ kostnad | Frekvens | Kommentarer |
---|---|---|---|
Läser in metadata | Medel | En gång i varje programdomän. | Modell- och mappningsmetadata som används av Entity Framework läses in i en MetadataWorkspace. Dessa metadata cachelagras globalt och är tillgängliga för andra instanser av ObjectContext i samma programdomän. |
Öppna databasanslutningen | Måttlig1 | Efter behov. | Eftersom en öppen anslutning till databasen förbrukar en värdefull resurs öppnar Entity Framework och stänger endast databasanslutningen efter behov. Du kan också uttryckligen öppna anslutningen. Mer information finns i Hantera Anslut ioner och transaktioner. |
Generera vyer | Högt | En gång i varje programdomän. (Kan förgenereras.) | Innan Entity Framework kan köra en fråga mot en konceptuell modell eller spara ändringar i datakällan måste den generera en uppsättning lokala frågevyer för att få åtkomst till databasen. På grund av den höga kostnaden för att generera dessa vyer kan du i förväg generera vyerna och lägga till dem i projektet vid designtillfället. Mer information finns i How to: Pre-Generate Views to Improve Query Performance (Så här skapar du vyer i förväg för att förbättra frågeprestanda). |
Förbereda frågan | Måttlig2 | En gång för varje unik fråga. | Inkluderar kostnaderna för att skapa frågekommandot, generera ett kommandoträd baserat på modell- och mappningsmetadata och definiera formen på de returnerade data. Eftersom både SQL-frågekommandon för entitet och LINQ-frågor nu cachelagras tar det mindre tid att köra samma fråga senare. Du kan fortfarande använda kompilerade LINQ-frågor för att minska den här kostnaden vid senare körningar och kompilerade frågor kan vara effektivare än LINQ-frågor som cachelagras automatiskt. Mer information finns i Kompilerade frågor (LINQ till entiteter). Allmän information om LINQ-frågekörning finns i LINQ till entiteter. Obs! LINQ för entitetsfrågor som tillämpar operatorn Enumerable.Contains på minnesinterna samlingar cachelagras inte automatiskt. Det är inte heller tillåtet att parametrisera minnesinterna samlingar i kompilerade LINQ-frågor. |
Köra frågan | Låg2 | En gång för varje fråga. | Kostnaden för att köra kommandot mot datakällan med hjälp av ADO.NET dataprovidern. Eftersom de flesta datakällor cachelagrar frågeplaner kan senare körningar av samma fråga ta ännu mindre tid. |
Inläsnings- och valideringstyper | Låg3 | En gång för varje ObjectContext instans. | Typer läses in och verifieras mot de typer som den konceptuella modellen definierar. |
Spårning | Låg3 | En gång för varje objekt som en fråga returnerar. 4 | Om en fråga använder sammanslagningsalternativet NoTracking påverkar inte den här fasen prestanda. Om frågan använder AppendOnlyalternativet , PreserveChanges, eller OverwriteChanges sammanslagning spåras frågeresultat i ObjectStateManager. En EntityKey genereras för varje spårat objekt som frågan returnerar och används för att skapa en ObjectStateEntry i ObjectStateManager. Om en befintlig ObjectStateEntry finns för EntityKeyreturneras det befintliga objektet. PreserveChangesOm alternativet , eller OverwriteChanges används uppdateras objektet innan det returneras. Mer information finns i Identitetsmatchning, Tillståndshantering och Ändringsspårning. |
Materialisera objekten | Måttlig3 | En gång för varje objekt som en fråga returnerar. 4 | Processen att läsa det returnerade DbDataReader objektet och skapa objekt och ange egenskapsvärden som baseras på värdena i varje instans av DbDataRecord klassen. Om objektet redan finns i ObjectContext och frågan använder AppendOnly eller PreserveChanges sammanfogar alternativ påverkar inte den här fasen prestanda. Mer information finns i Identitetsmatchning, Tillståndshantering och Ändringsspårning. |
1 När en datakällaprovider implementerar anslutningspooler fördelas kostnaden för att öppna en anslutning över poolen. .NET-providern för SQL Server stöder anslutningspooler.
2 Kostnadsökningar med ökad frågekomplexitet.
3 Den totala kostnaden ökar proportionellt mot antalet objekt som returneras av frågan.
4 Den här kostnaden krävs inte för EntityClient-frågor eftersom EntityClient-frågor returnerar en EntityDataReader i stället för objekt. Mer information finns i EntityClient-providern för Entity Framework.
Ytterligare överväganden
Följande är andra överväganden som kan påverka prestandan för Entity Framework-program.
Frågekörning
Eftersom frågor kan vara resursintensiva bör du tänka på vid vilken tidpunkt i koden och på vilken dator en fråga körs.
Uppskjuten kontra omedelbar körning
När du skapar en ObjectQuery<T> ELLER LINQ-fråga kanske frågan inte körs omedelbart. Frågekörningen skjuts upp tills resultaten behövs, till exempel under en foreach
(C#) eller For Each
(Visual Basic)-uppräkning eller när den har tilldelats för att fylla en List<T> samling. Frågekörningen börjar omedelbart när du anropar Execute metoden på en ObjectQuery<T> eller när du anropar en LINQ-metod som returnerar en singleton-fråga, till exempel First eller Any. Mer information finns i Objektfrågor och Frågekörning (LINQ till entiteter).
Körning på klientsidan av LINQ-frågor
Även om körningen av en LINQ-fråga sker på den dator som är värd för datakällan kan vissa delar av en LINQ-fråga utvärderas på klientdatorn. Mer information finns i avsnittet Lagringskörning i Frågekörning (LINQ till entiteter).
Fråge- och mappningskomplexitet
Komplexiteten i enskilda frågor och mappningen i entitetsmodellen har en betydande effekt på frågeprestandan.
Mappa komplexitet
Modeller som är mer komplexa än en enkel en-till-en-mappning mellan entiteter i den konceptuella modellen och tabeller i lagringsmodellen genererar mer komplexa kommandon än modeller som har en en-till-en-mappning.
Frågekomplexitet
Frågor som kräver ett stort antal kopplingar i kommandona som körs mot datakällan eller som returnerar en stor mängd data kan påverka prestandan på följande sätt:
Frågor mot en konceptuell modell som verkar enkel kan leda till körning av mer komplexa frågor mot datakällan. Detta kan inträffa eftersom Entity Framework översätter en fråga mot en konceptuell modell till en motsvarande fråga mot datakällan. När en enskild entitet som anges i den konceptuella modellen mappar till mer än en tabell i datakällan, eller när en relation mellan entiteter mappas till en kopplingstabell, kan frågekommandot som körs mot datakällfrågan kräva en eller flera kopplingar.
Kommentar
ToTraceString Använd metoden ObjectQuery<T> för klasserna eller EntityCommand för att visa kommandona som körs mot datakällan för en viss fråga. Mer information finns i Så här: Visa store-kommandon.
Kapslade SQL-frågor för entitet kan skapa kopplingar på servern och kan returnera ett stort antal rader.
Följande är ett exempel på en kapslad fråga i en projektionssats:
SELECT c, (SELECT c, (SELECT c FROM AdventureWorksModel.Vendor AS c ) As Inner2 FROM AdventureWorksModel.JobCandidate AS c ) As Inner1 FROM AdventureWorksModel.EmployeeDepartmentHistory AS c
Sådana frågor gör dessutom att frågepipelinen genererar en enda fråga med duplicering av objekt mellan kapslade frågor. På grund av detta kan en enskild kolumn dupliceras flera gånger. På vissa databaser, inklusive SQL Server, kan detta göra att TempDB-tabellen blir mycket stor, vilket kan minska serverprestandan. Du bör vara försiktig när du kör kapslade frågor.
Alla frågor som returnerar en stor mängd data kan orsaka lägre prestanda om klienten utför åtgärder som förbrukar resurser på ett sätt som är proportionellt mot resultatuppsättningens storlek. I sådana fall bör du överväga att begränsa mängden data som returneras av frågan. Mer information finns i How to: Page Through Query Results (Så här gör du: Bläddra igenom frågeresultat).
Kommandon som genereras automatiskt av Entity Framework kan vara mer komplexa än liknande kommandon som uttryckligen skrivs av en databasutvecklare. Om du behöver explicit kontroll över de kommandon som körs mot datakällan kan du överväga att definiera en mappning till en tabellvärdesfunktion eller en lagrad procedur.
Relationer
För optimal frågeprestanda måste du definiera relationer mellan entiteter både som associationer i entitetsmodellen och som logiska relationer i datakällan.
Frågesökvägar
När du kör ett ObjectQuery<T>returneras som standard inte relaterade objekt (även om objekt som representerar själva relationerna är det). Du kan läsa in relaterade objekt på något av tre sätt:
Ange frågesökvägen innan den ObjectQuery<T> körs.
Load
Anropa metoden för navigeringsegenskapen som objektet exponerar.Ange alternativet LazyLoadingEnabled på ObjectContext till
true
. Observera att detta görs automatiskt när du genererar objektlagerkod med Entitetsdatamodelldesignern. Mer information finns i Översikt över genererad kod.
Tänk på att det finns en kompromiss mellan antalet begäranden mot databasen och mängden data som returneras i en enda fråga när du överväger vilket alternativ du vill använda. Mer information finns i Läsa in relaterade objekt.
Använda frågesökvägar
Frågesökvägar definierar grafen över objekt som en fråga returnerar. När du definierar en frågesökväg krävs endast en enskild begäran mot databasen för att returnera alla objekt som sökvägen definierar. Om du använder frågesökvägar kan komplexa kommandon köras mot datakällan från till synes enkla objektfrågor. Detta beror på att en eller flera kopplingar krävs för att returnera relaterade objekt i en enda fråga. Den här komplexiteten är större i frågor mot en komplex entitetsmodell, till exempel en entitet med arv eller en sökväg som innehåller många-till-många-relationer.
Kommentar
ToTraceString Använd metoden för att se kommandot som genereras av en ObjectQuery<T>. Mer information finns i Så här: Visa store-kommandon.
När en frågesökväg innehåller för många relaterade objekt eller om objekten innehåller för mycket raddata kanske datakällan inte kan slutföra frågan. Detta inträffar om frågan kräver mellanliggande tillfällig lagring som överskrider datakällans funktioner. När detta inträffar kan du minska datakällans komplexitet genom att uttryckligen läsa in relaterade objekt.
Läser uttryckligen in relaterade objekt
Du kan uttryckligen läsa in relaterade objekt genom att anropa Load
metoden på en navigeringsegenskap som returnerar en EntityCollection<TEntity> eller EntityReference<TEntity>. Explicit inläsning av objekt kräver en rundtur till databasen varje gång Load
som anropas.
Kommentar
Om du anropar Load
när du loopar genom en samling returnerade objekt, till exempel när du använder -instruktionen foreach
(For Each
i Visual Basic), måste den datakällsspecifika providern ha stöd för flera aktiva resultatuppsättningar på en enda anslutning. För en SQL Server-databas måste du ange ett värde MultipleActiveResultSets = true
för i providern anslutningssträng.
Du kan också använda LoadProperty metoden när det inte finns några EntityCollection<TEntity> eller EntityReference<TEntity> egenskaper för entiteter. Detta är användbart när du använder POCO-entiteter.
Även om explicit inläsning av relaterade objekt minskar antalet kopplingar och minskar mängden redundanta data, Load
krävs upprepade anslutningar till databasen, vilket kan bli kostsamt när du uttryckligen läser in ett stort antal objekt.
Spara ändringar
När du anropar SaveChanges metoden för ett ObjectContextgenereras ett separat kommando för att skapa, uppdatera eller ta bort för varje tillagt, uppdaterat eller borttaget objekt i kontexten. Dessa kommandon körs på datakällan i en enda transaktion. Precis som med frågor beror prestanda för åtgärder för att skapa, uppdatera och ta bort på komplexiteten i mappningen i den konceptuella modellen.
Distribuerade transaktioner
Åtgärder i en explicit transaktion som kräver resurser som hanteras av DTC (Distributed Transaction Coordinator) blir mycket dyrare än en liknande åtgärd som inte kräver DTC. Befordran till DTC sker i följande situationer:
En explicit transaktion med en åtgärd mot en SQL Server 2000-databas eller annan datakälla som alltid höjer upp explicita transaktioner till DTC.
En explicit transaktion med en åtgärd mot SQL Server 2005 när anslutningen hanteras av Entity Framework. Detta beror på att SQL Server 2005 befordras till en DTC när en anslutning stängs och öppnas igen i en enda transaktion, vilket är standardbeteendet för Entity Framework. Den här DTC-befordran sker inte när du använder SQL Server 2008. För att undvika den här befordran när du använder SQL Server 2005 måste du uttryckligen öppna och stänga anslutningen i transaktionen. Mer information finns i Hantera Anslut ioner och transaktioner.
En explicit transaktion används när en eller flera åtgärder körs i en System.Transactions transaktion. Mer information finns i Hantera Anslut ioner och transaktioner.
Strategier för att förbättra prestanda
Du kan förbättra den övergripande prestandan för frågor i Entity Framework med hjälp av följande strategier.
Generera vyer i förväg
Att generera vyer baserat på en entitetsmodell är en betydande kostnad första gången ett program kör en fråga. Använd verktyget EdmGen.exe för att i förväg generera vyer som en Visual Basic- eller C#-kodfil som kan läggas till i projektet under designen. Du kan också använda verktyg för omvandling av textmallar för att generera förkompilerade vyer. Förgenererade vyer verifieras vid körning för att säkerställa att de är konsekventa med den aktuella versionen av den angivna entitetsmodellen. Mer information finns i How to: Pre-Generate Views to Improve Query Performance (Så här skapar du vyer i förväg för att förbättra frågeprestanda).
När du arbetar med mycket stora modeller gäller följande:
.NET-metadataformatet begränsar antalet användarsträngstecken i en viss binär fil till 16 777 215 (0xFFFFFF). Om du genererar vyer för en mycket stor modell och vyfilen når den här storleksgränsen får du kompileringsfelet "Inget logiskt utrymme kvar för att skapa fler användarsträngar". Den här storleksbegränsningen gäller för alla hanterade binärfiler. Mer information finns i bloggen som visar hur du undviker felet när du arbetar med stora och komplexa modeller.
Överväg att använda kopplingsalternativet NoTracking för frågor
Det krävs en kostnad för att spåra returnerade objekt i objektkontexten. Att identifiera ändringar i objekt och se till att flera begäranden för samma logiska entitet returnerar samma objektinstans kräver att objekt kopplas till en ObjectContext instans. Om du inte planerar att göra uppdateringar eller borttagningar till objekt och inte kräver identitetshantering kan du överväga att använda kopplingsalternativen NoTracking när du kör frågor.
Returnera rätt mängd data
I vissa scenarier går det mycket snabbare att ange en frågesökväg med hjälp av Include metoden eftersom det kräver färre tur- och returresor till databasen. I andra scenarier kan dock ytterligare turer till databasen för att läsa in relaterade objekt vara snabbare eftersom de enklare frågorna med färre kopplingar resulterar i mindre redundans av data. Därför rekommenderar vi att du testar prestanda för olika sätt att hämta relaterade objekt. Mer information finns i Läsa in relaterade objekt.
Om du vill undvika att returnera för mycket data i en enda fråga bör du överväga att växla resultatet av frågan till mer hanterbara grupper. Mer information finns i How to: Page Through Query Results (Så här gör du: Bläddra igenom frågeresultat).
Begränsa omfånget för ObjectContext
I de flesta fall bör du skapa en ObjectContext instans i en using
-instruktion (Using…End Using
i Visual Basic). Detta kan öka prestandan genom att se till att de resurser som är associerade med objektkontexten tas bort automatiskt när koden avslutar instruktionsblocket. Men när kontroller är bundna till objekt som hanteras av objektkontexten bör instansen ObjectContext underhållas så länge bindningen behövs och tas bort manuellt. Mer information finns i Hantera Anslut ioner och transaktioner.
Överväg att öppna databasanslutningen manuellt
När programmet kör en serie objektfrågor eller ofta anropar SaveChanges för att spara åtgärder för att skapa, uppdatera och ta bort till datakällan måste Entity Framework kontinuerligt öppna och stänga anslutningen till datakällan. I dessa situationer bör du överväga att manuellt öppna anslutningen i början av dessa åtgärder och antingen stänga eller ta bort anslutningen när åtgärderna är slutförda. Mer information finns i Hantera Anslut ioner och transaktioner.
Prestandadata
Vissa prestandadata för Entity Framework publiceras i följande inlägg på ADO.NET teambloggen: