Anteckning
Å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.
Språkintegrerad fråga (LINQ) innehåller många komplexa operatorer som kombinerar flera datakällor eller utför komplex bearbetning. Alla LINQ-operatorer har inte lämpliga översättningar på serversidan. Ibland översätts en fråga i ett formulär till servern, men om den skrivs i ett annat formulär översätts den inte även om resultatet är detsamma. På den här sidan beskrivs några av de komplexa operatorerna och deras varianter som stöds. I framtida versioner kan vi känna igen fler mönster och lägga till motsvarande översättningar. Det är också viktigt att komma ihåg att översättningsstöd varierar mellan leverantörer. En viss fråga, som översätts i SqlServer, kanske inte fungerar för SQLite-databaser.
Tips/Råd
Du kan visa den här artikelns exempel på GitHub.
Ansluta
Med LINQ Join-operatorn kan du ansluta två datakällor baserat på nyckelväljaren för varje källa, vilket genererar en tuppeln med värden när nyckeln matchar. Det översätts naturligt till INNER JOIN i relation till relationsdatabaser. Linq-kopplingen har yttre och inre nyckelväljare, men databasen kräver ett enda kopplingsvillkor. EF Core genererar därför ett kopplingsvillkor genom att jämföra den yttre nyckelväljaren med den inre nyckelväljaren för likhet.
var query = from photo in context.Set<PersonPhoto>()
join person in context.Set<Person>()
on photo.PersonPhotoId equals person.PhotoId
select new { person, photo };
SELECT [p].[PersonId], [p].[Name], [p].[PhotoId], [p0].[PersonPhotoId], [p0].[Caption], [p0].[Photo]
FROM [PersonPhoto] AS [p0]
INNER JOIN [Person] AS [p] ON [p0].[PersonPhotoId] = [p].[PhotoId]
Om nyckelväljarna dessutom är anonyma typer genererar EF Core ett kopplingsvillkor för att jämföra likhetskomponentvis.
var query = from photo in context.Set<PersonPhoto>()
join person in context.Set<Person>()
on new { Id = (int?)photo.PersonPhotoId, photo.Caption }
equals new { Id = person.PhotoId, Caption = "SN" }
select new { person, photo };
SELECT [p].[PersonId], [p].[Name], [p].[PhotoId], [p0].[PersonPhotoId], [p0].[Caption], [p0].[Photo]
FROM [PersonPhoto] AS [p0]
INNER JOIN [Person] AS [p] ON ([p0].[PersonPhotoId] = [p].[PhotoId] AND ([p0].[Caption] = N'SN'))
Gruppanslutning
Med LINQ GroupJoin-operatorn kan du ansluta två datakällor som liknar Join, men den skapar en grupp med inre värden för matchande yttre element. Om du kör en fråga som i följande exempel genereras ett resultat av Blog & IEnumerable<Post>. Eftersom databaser (särskilt relationsdatabaser) inte kan representera en samling objekt på klientsidan översätts inte GroupJoin till servern i många fall. Det kräver att du hämtar alla data från servern för att göra GroupJoin utan en särskild väljare (första frågan nedan). Men om väljaren begränsar data som väljs kan hämtning av alla data från servern orsaka prestandaproblem (andra frågan nedan). Det är därför EF Core inte översätter GroupJoin.
var query = from b in context.Set<Blog>()
join p in context.Set<Post>()
on b.BlogId equals p.BlogId into grouping
select new { b, grouping };
var query = from b in context.Set<Blog>()
join p in context.Set<Post>()
on b.BlogId equals p.BlogId into grouping
select new { b, Posts = grouping.Where(p => p.Content.Contains("EF")).ToList() };
SelectMany
Med LINQ SelectMany-operatorn kan du räkna upp en samlingsväljare för varje yttre element och generera tupplar med värden från varje datakälla. På sätt och vis är det en koppling men utan villkor, så varje yttre element är anslutet till ett element från samlingskällan. Beroende på hur samlingsväljaren är relaterad till den yttre datakällan kan SelectMany översättas till olika frågor på serversidan.
Samlingsväljaren pekar inte på yttre kontext
När samlingsväljaren inte refererar till något från den yttre källan blir resultatet en kartesisk produkt av båda datakällorna. Det översätts till CROSS JOIN i relationsdatabaser.
var query = from b in context.Set<Blog>()
from p in context.Set<Post>()
select new { b, p };
SELECT [b].[BlogId], [b].[OwnerId], [b].[Rating], [b].[Url], [p].[PostId], [p].[AuthorId], [p].[BlogId], [p].[Content], [p].[Rating], [p].[Title]
FROM [Blogs] AS [b]
CROSS JOIN [Posts] AS [p]
Samlingsväljaren refererar till yttre i en where-sats
När samlingsväljaren har en where-sats, som refererar till det yttre elementet, översätter EF Core den till en databaskoppling och använder predikatet som kopplingsvillkor. Normalt uppstår det här fallet när du använder samlingsnavigering på det yttre elementet som samlingsväljare. Om samlingen är tom för ett yttre element genereras inga resultat för det yttre elementet. Men om DefaultIfEmpty används på samlingsväljaren ansluts det yttre elementet med standardvärdet för det inre elementet. På grund av den här skillnaden översätts den här typen av frågor till INNER JOIN i avsaknad av DefaultIfEmpty och LEFT JOIN när DefaultIfEmpty som tillämpas.
var query = from b in context.Set<Blog>()
from p in context.Set<Post>().Where(p => b.BlogId == p.BlogId)
select new { b, p };
var query2 = from b in context.Set<Blog>()
from p in context.Set<Post>().Where(p => b.BlogId == p.BlogId).DefaultIfEmpty()
select new { b, p };
SELECT [b].[BlogId], [b].[OwnerId], [b].[Rating], [b].[Url], [p].[PostId], [p].[AuthorId], [p].[BlogId], [p].[Content], [p].[Rating], [p].[Title]
FROM [Blogs] AS [b]
INNER JOIN [Posts] AS [p] ON [b].[BlogId] = [p].[BlogId]
SELECT [b].[BlogId], [b].[OwnerId], [b].[Rating], [b].[Url], [p].[PostId], [p].[AuthorId], [p].[BlogId], [p].[Content], [p].[Rating], [p].[Title]
FROM [Blogs] AS [b]
LEFT JOIN [Posts] AS [p] ON [b].[BlogId] = [p].[BlogId]
Samlingsväljaren refererar till det yttre sammanhanget i ett fall utan where.
När samlingsväljaren refererar till det yttre elementet, som inte finns i en where-sats (som fallet ovan), översätts det inte till en databaskoppling. Därför måste vi utvärdera samlingsväljaren för varje yttre element. Det motsvarar APPLY operationer i många relationsdatabaser. Om samlingen är tom för ett yttre element genereras inga resultat för det yttre elementet. Men om DefaultIfEmpty används på samlingsväljaren ansluts det yttre elementet med standardvärdet för det inre elementet. På grund av den här skillnaden översätts den här typen av frågor till CROSS APPLY i avsaknad av DefaultIfEmpty och OUTER APPLY när DefaultIfEmpty som tillämpas. Vissa databaser som SQLite stöder APPLY inte operatorer, så den här typen av fråga kanske inte översätts.
var query = from b in context.Set<Blog>()
from p in context.Set<Post>().Select(p => b.Url + "=>" + p.Title)
select new { b, p };
var query2 = from b in context.Set<Blog>()
from p in context.Set<Post>().Select(p => b.Url + "=>" + p.Title).DefaultIfEmpty()
select new { b, p };
SELECT [b].[BlogId], [b].[OwnerId], [b].[Rating], [b].[Url], ([b].[Url] + N'=>') + [p].[Title] AS [p]
FROM [Blogs] AS [b]
CROSS APPLY [Posts] AS [p]
SELECT [b].[BlogId], [b].[OwnerId], [b].[Rating], [b].[Url], ([b].[Url] + N'=>') + [p].[Title] AS [p]
FROM [Blogs] AS [b]
OUTER APPLY [Posts] AS [p]
GruppEra
LINQ GroupBy-operatorer skapar ett resultat av typen IGrouping<TKey, TElement> där TKey och TElement kan vara valfri godtycklig typ. Dessutom IGrouping implementerar IEnumerable<TElement>, vilket innebär att du kan skriva över den med hjälp av alla LINQ-operatorer efter gruppering. Eftersom ingen databasstruktur kan representera en IGrouping, har GroupBy-operatorer ingen översättning i de flesta fall. När en aggregeringsoperator tillämpas på varje grupp, som returnerar en skalär, kan den översättas till SQL GROUP BY i relationsdatabaser. SQL GROUP BY är också restriktivt. Det kräver att du endast grupperar efter skalära värden. Projektionen får bara innehålla grupperingsnyckelkolumner eller aggregeringar som tillämpas över en kolumn. EF Core identifierar det här mönstret och översätter det till servern, som i följande exempel:
var query = from p in context.Set<Post>()
group p by p.AuthorId
into g
select new { g.Key, Count = g.Count() };
SELECT [p].[AuthorId] AS [Key], COUNT(*) AS [Count]
FROM [Posts] AS [p]
GROUP BY [p].[AuthorId]
EF Core översätter även frågor där en aggregerad operator i gruppering används i en Where- eller OrderBy-operator (eller annan ordnings-operator) i LINQ. Den använder HAVING satsen i SQL för where-satsen. Delen av frågan innan du tillämpar GroupBy-operatorn kan vara vilken komplex fråga som helst så länge den kan översättas till servern. När du tillämpar aggregerade operatorer på en grupperingsfråga för att ta bort grupperingar från den resulterande källan kan du dessutom skriva ovanpå den som andra frågor.
var query = from p in context.Set<Post>()
group p by p.AuthorId
into g
where g.Count() > 0
orderby g.Key
select new { g.Key, Count = g.Count() };
SELECT [p].[AuthorId] AS [Key], COUNT(*) AS [Count]
FROM [Posts] AS [p]
GROUP BY [p].[AuthorId]
HAVING COUNT(*) > 0
ORDER BY [p].[AuthorId]
De aggregeringsoperatorer som EF Core stöder är följande
| .NÄT | SQL |
|---|---|
| Genomsnitt(x => x.Egenskap) | Medelvärde(Egenskap) |
| Räkna() | COUNT(*) |
| LongCount() | COUNT(*) |
| Max(x => x.Egenskap) | MAX(Egenskap) |
| Min(x => x.Egenskap) | MIN(egenskap) |
| Sum(x => x.Property) | SUM(Property) |
Ytterligare aggregeringsoperatorer kan stödjas. Mer funktionsmappningar finns i providerdokumenten.
Även om det inte finns någon databasstruktur som representerar en IGrouping, kan EF Core 7.0 och senare i vissa fall skapa grupperingarna när resultaten returneras från databasen. Det här liknar hur operatorn Include fungerar när du inkluderar relaterade samlingar. Följande LINQ-fråga använder GroupBy-operatorn för att gruppera resultatet efter värdet för egenskapen Price.
var query = context.Books.GroupBy(s => s.Price);
SELECT [b].[Price], [b].[Id], [b].[AuthorId]
FROM [Books] AS [b]
ORDER BY [b].[Price]
I det här fallet översätts inte GroupBy-operatorn direkt till en GROUP BY -sats i SQL, utan i stället skapar EF Core grupperingar när resultatet returneras från servern.
Vänster sammanfogning
Även om vänsterkoppling inte är en LINQ-operator har relationsdatabaser begreppet vänsterkoppling som ofta används i frågor. Ett visst mönster i LINQ-frågor ger samma resultat som ett LEFT JOIN på servern. EF Core identifierar sådana mönster och genererar motsvarande LEFT JOIN på serversidan. Mönstret innebär att skapa en GroupJoin mellan båda datakällorna och sedan platta ut grupperingen med hjälp av SelectMany-operatorn med DefaultIfEmpty på grupperingskällan för att matcha null när den inre källan inte har något relaterat element. I följande exempel visas hur det mönstret ser ut och vad det genererar.
var query = from b in context.Set<Blog>()
join p in context.Set<Post>()
on b.BlogId equals p.BlogId into grouping
from p in grouping.DefaultIfEmpty()
select new { b, p };
SELECT [b].[BlogId], [b].[OwnerId], [b].[Rating], [b].[Url], [p].[PostId], [p].[AuthorId], [p].[BlogId], [p].[Content], [p].[Rating], [p].[Title]
FROM [Blogs] AS [b]
LEFT JOIN [Posts] AS [p] ON [b].[BlogId] = [p].[BlogId]
Mönstret ovan skapar en komplex struktur i uttrycksträdet. På grund av detta kräver EF Core att du jämnar ut gruppresultatet för GroupJoin-operatorn i ett steg omedelbart efter operatorn. Även om GroupJoin-DefaultIfEmpty-SelectMany används på ett annat sätt kanske vi ändå inte identifierar det som en vänsteranslutning.