Kommentar
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Inledning
SQL-databaser fungerar på 3-värderad logik (true, false, null) när du utför jämförelser, i motsats till den booleska logiken i C#. När DU översätter LINQ-frågor till SQL försöker EF Core kompensera för skillnaden genom att införa ytterligare null-kontroller för vissa element i frågan.
För att illustrera detta ska vi definiera följande entitet:
public class NullSemanticsEntity
{
public int Id { get; set; }
public int Int { get; set; }
public int? NullableInt { get; set; }
public string String1 { get; set; }
public string String2 { get; set; }
}
och utfärda flera frågor:
var query1 = context.Entities.Where(e => e.Id == e.Int);
var query2 = context.Entities.Where(e => e.Id == e.NullableInt);
var query3 = context.Entities.Where(e => e.Id != e.NullableInt);
var query4 = context.Entities.Where(e => e.String1 == e.String2);
var query5 = context.Entities.Where(e => e.String1 != e.String2);
De två första frågorna ger enkla jämförelser. I den första frågan är båda kolumnerna inte nullbara, så null-kontroller behövs inte. I den andra frågan kan NullableInt innehålla null, men Id är inte nullable; att jämföra null med icke-null ger null som ett resultat, vilket skulle filtreras bort av WHERE-operationen. Därför behövs inga ytterligare villkor heller.
SELECT [e].[Id], [e].[Int], [e].[NullableInt], [e].[String1], [e].[String2]
FROM [Entities] AS [e]
WHERE [e].[Id] = [e].[Int]
SELECT [e].[Id], [e].[Int], [e].[NullableInt], [e].[String1], [e].[String2]
FROM [Entities] AS [e]
WHERE [e].[Id] = [e].[NullableInt]
Den tredje frågan introducerar en null-kontroll. När NullableInt är null ger jämförelsen Id <> NullableIntnull, vilket skulle filtreras bort av WHERE-åtgärden. Men ur det booleska logikperspektivet bör det här fallet returneras som en del av resultatet. Ef Core lägger därför till den nödvändiga kontrollen för att säkerställa detta.
SELECT [e].[Id], [e].[Int], [e].[NullableInt], [e].[String1], [e].[String2]
FROM [Entities] AS [e]
WHERE ([e].[Id] <> [e].[NullableInt]) OR [e].[NullableInt] IS NULL
Frågor fyra och fem visar mönstret när båda kolumnerna är nullbara. Det är värt att notera att åtgärden <> ger en mer komplicerad (och potentiellt långsammare) fråga än åtgärden == .
SELECT [e].[Id], [e].[Int], [e].[NullableInt], [e].[String1], [e].[String2]
FROM [Entities] AS [e]
WHERE ([e].[String1] = [e].[String2]) OR ([e].[String1] IS NULL AND [e].[String2] IS NULL)
SELECT [e].[Id], [e].[Int], [e].[NullableInt], [e].[String1], [e].[String2]
FROM [Entities] AS [e]
WHERE (([e].[String1] <> [e].[String2]) OR ([e].[String1] IS NULL OR [e].[String2] IS NULL)) AND ([e].[String1] IS NOT NULL OR [e].[String2] IS NOT NULL)
Behandling av nullbara värden i funktioner
Många funktioner i SQL kan bara returnera ett null resultat om några av deras argument är null. EF Core drar nytta av detta för att skapa effektivare frågor.
Frågan nedan illustrerar optimeringen:
var query = context.Entities.Where(e => e.String1.Substring(0, e.String2.Length) == null);
Den genererade SQL:en är följande (vi behöver inte utvärdera funktionen eftersom den SUBSTRING bara är null när något av argumenten är null.):
SELECT [e].[Id], [e].[Int], [e].[NullableInt], [e].[String1], [e].[String2]
FROM [Entities] AS [e]
WHERE [e].[String1] IS NULL OR [e].[String2] IS NULL
Optimeringen kan också användas för användardefinierade funktioner. Mer information finns på sidan för användardefinierad funktionsmappning .
Skriva högpresterande frågor
Det är enklare och snabbare att jämföra icke-nullbara kolumner än att jämföra nullbara kolumner. Överväg att markera kolumner som icke-nullbara när det är möjligt.
Det är enklare och snabbare att söka efter likhet (
==) än att söka efter icke-likhet (!=), eftersom frågan inte behöver skilja mellannullochfalseresultat. Använd likhetsjämförelse när det är möjligt. Men att bara negera==jämförelsen är i praktiken detsamma som!=, så det resulterar inte i prestandaförbättringar.I vissa fall är det möjligt att förenkla en komplex jämförelse genom att filtrera bort
nullvärden från en kolumn explicit , till exempel när inganullvärden finns eller dessa värden inte är relevanta i resultatet. Tänk på följande exempel:
var query1 = context.Entities.Where(e => e.String1 != e.String2 || e.String1.Length == e.String2.Length);
var query2 = context.Entities.Where(
e => e.String1 != null && e.String2 != null && (e.String1 != e.String2 || e.String1.Length == e.String2.Length));
Dessa frågor skapar följande SQL:
SELECT [e].[Id], [e].[Int], [e].[NullableInt], [e].[String1], [e].[String2]
FROM [Entities] AS [e]
WHERE ((([e].[String1] <> [e].[String2]) OR ([e].[String1] IS NULL OR [e].[String2] IS NULL)) AND ([e].[String1] IS NOT NULL OR [e].[String2] IS NOT NULL)) OR ((CAST(LEN([e].[String1]) AS int) = CAST(LEN([e].[String2]) AS int)) OR ([e].[String1] IS NULL AND [e].[String2] IS NULL))
SELECT [e].[Id], [e].[Int], [e].[NullableInt], [e].[String1], [e].[String2]
FROM [Entities] AS [e]
WHERE ([e].[String1] IS NOT NULL AND [e].[String2] IS NOT NULL) AND (([e].[String1] <> [e].[String2]) OR (CAST(LEN([e].[String1]) AS int) = CAST(LEN([e].[String2]) AS int)))
I den andra frågan null filtreras resultaten uttryckligen bort från String1 kolumnen. EF Core kan på ett säkert sätt behandla String1 kolumnen som icke-nullbar under jämförelsen, vilket resulterar i en enklare fråga.
Använda relations-null-semantik
Det går att inaktivera nolljämförelsekompensationen och använda relations-null-semantik direkt. Detta kan göras genom att anropa UseRelationalNulls(true) metoden på alternativverktyget inuti OnConfiguring metoden:
new SqlServerDbContextOptionsBuilder(optionsBuilder).UseRelationalNulls();
Varning
När du använder relations-null-semantik har dina LINQ-frågor inte längre samma innebörd som de gör i C#, och kan ge andra resultat än förväntat. Var försiktig när du använder det här läget.