Uwaga
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Zapytanie zintegrowane z językiem (LINQ) zawiera wiele złożonych operatorów, które łączą wiele źródeł danych lub wykonuje złożone przetwarzanie. Nie wszystkie operatory LINQ mają odpowiednie tłumaczenia po stronie serwera. Czasami zapytanie w jednym formularzu przekłada się na serwer, ale jeśli zostało zapisane w innym formularzu, nie przekłada się nawet wtedy, gdy wynik jest taki sam. Na tej stronie opisano niektóre złożone operatory i ich obsługiwane odmiany. W przyszłych wersjach możemy rozpoznać więcej wzorców i dodać odpowiednie tłumaczenia. Należy również pamiętać, że obsługa tłumaczenia różni się między dostawcami. Określone zapytanie, które jest tłumaczone w programie SqlServer, może nie działać w przypadku baz danych SQLite.
Wskazówka
Przykład z tego artykułu można zobaczyć w witrynie GitHub.
Dołączyć
Operator łączenia LINQ umożliwia połączenie dwóch źródeł danych na podstawie selektora klucza dla każdego źródła, generując krotkę wartości, gdy klucz się zgadza. Naturalnie tłumaczy się jako INNER JOIN
w relacyjnych bazach danych. Chociaż sprzężenie LINQ ma selektory kluczy zewnętrznych i wewnętrznych, baza danych wymaga jednego warunku sprzężenia. Dlatego program EF Core generuje warunek sprzężenia, porównując selektor kluczy zewnętrznych z selektorem klucza wewnętrznego pod kątem równości.
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]
Ponadto, jeśli selektory kluczy są typami anonimowymi, program EF Core generuje warunek sprzężenia, który porównuje składniki pod względem równości składnik po składniku.
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'))
Łączenie grup
Operator LINQ GroupJoin umożliwia połączenie dwóch źródeł danych podobnie do Join, ale tworzy grupę wartości wewnętrznych do dopasowywania elementów zewnętrznych. Wykonanie zapytania, takiego jak w poniższym przykładzie, powoduje wygenerowanie wyniku parametru Blog
& IEnumerable<Post>
. Ponieważ bazy danych (zwłaszcza relacyjne bazy danych) nie mają możliwości reprezentowania kolekcji obiektów po stronie klienta, metoda GroupJoin nie jest tłumaczona na serwer w wielu przypadkach. Wymaga to pobrania wszystkich danych z serwera do wykonania GroupJoin bez specjalnego selektora (pierwsze zapytanie poniżej). Jeśli jednak selektor ogranicza wybrane dane, pobieranie wszystkich danych z serwera może powodować problemy z wydajnością (drugie zapytanie poniżej). Dlatego program EF Core nie tłumaczy elementu 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() };
Wybierz wiele
Operator LINQ SelectMany umożliwia przeprowadzanie enumeracji za pomocą selektora kolekcji dla każdego elementu zewnętrznego i generowanie krotek wartości z każdego źródła danych. W pewien sposób jest to połączenie, ale bez żadnego warunku, więc każdy element zewnętrzny jest połączony z elementem ze źródła kolekcji. W zależności od tego, jak selektor kolekcji jest powiązany z zewnętrznym źródłem danych, SelectMany może przekładać się na różne zapytania po stronie serwera.
Selektor kolekcji nie odwołuje się do elementu zewnętrznego
Gdy selektor kolekcji nie odwołuje się do niczego ze źródła zewnętrznego, wynikiem jest kartezjański produkt obu źródeł danych. Przekłada się na CROSS JOIN
w relacyjnych bazach danych.
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]
Selektor kolekcji odwołuje się do elementów zewnętrznych w klauzuli where
Gdy wybór kolekcji zawiera klauzulę where, która odwołuje się do elementu zewnętrznego, EF Core tłumaczy ją na łączenie z bazą danych i używa predykatu jako warunku łączenia. Zwykle ta sytuacja ma miejsce podczas korzystania z nawigacji kolekcji na zewnętrznym elemencie jako selektorze kolekcji. Jeśli kolekcja jest pusta dla elementu zewnętrznego, nie zostaną wygenerowane żadne wyniki dla tego elementu zewnętrznego.
DefaultIfEmpty
Jeśli jednak zostanie zastosowany do selektora kolekcji, element zewnętrzny zostanie połączony z wartością domyślną elementu wewnętrznego. Ze względu na to rozróżnienie tego rodzaju zapytania przekładają się na INNER JOIN
w braku DefaultIfEmpty
i LEFT JOIN
kiedy DefaultIfEmpty
jest stosowane.
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]
Selektor kolekcji odwołuje się do zewnętrznego elementu w przypadku, który nie jest zapytaniem „where”.
Gdy selektor kolekcji odwołuje się do elementu zewnętrznego, który nie jest zawarty w klauzuli where (jak w powyższym przypadku), nie jest przekształcany w sprzężenie bazy danych. Dlatego musimy ocenić selektor zbioru dla każdego elementu zewnętrznego. Przekłada się na operacje APPLY
w wielu relacyjnych bazach danych. Jeśli kolekcja jest pusta dla elementu zewnętrznego, nie zostaną wygenerowane żadne wyniki dla tego elementu zewnętrznego.
DefaultIfEmpty
Jeśli jednak zostanie zastosowany do selektora kolekcji, element zewnętrzny zostanie połączony z wartością domyślną elementu wewnętrznego. Ze względu na to rozróżnienie tego rodzaju zapytania przekładają się na CROSS APPLY
w braku DefaultIfEmpty
i OUTER APPLY
kiedy DefaultIfEmpty
jest stosowane. Niektóre bazy danych, takie jak SQLite, nie obsługują APPLY
operatorów, więc tego rodzaju zapytanie może nie zostać przetłumaczone.
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]
Grupuj wg
Operatory LINQ GroupBy tworzą wynik typu IGrouping<TKey, TElement>
, w którym TKey
i TElement
może być dowolnym typem. Ponadto IGrouping
implementuje IEnumerable<TElement>
, co oznacza, że można na nim operować przy użyciu dowolnego operatora LINQ po zgrupowaniu. Ponieważ żadna struktura bazy danych nie może reprezentować IGrouping
, operatory GroupBy nie mają tłumaczenia w większości przypadków. Gdy operator agregacji jest stosowany do każdej grupy i zwraca skalarną wartość, można go przetłumaczyć na język SQL GROUP BY
w relacyjnych bazach danych.
GROUP BY
Sql również jest restrykcyjny. Wymaga to grupowania tylko według wartości skalarnych. Projekcja może zawierać tylko kolumny klucza grupowania lub dowolną agregację zastosowaną w kolumnie. Program EF Core identyfikuje ten wzorzec i tłumaczy go na serwer, jak w poniższym przykładzie:
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]
Program EF Core tłumaczy również zapytania, w których operator agregacji stosowany na grupowaniu pojawia się w operatorach LINQ, takich jak Where lub OrderBy (lub w innej kolejności). Używa klauzuli HAVING
w SQL dla klauzuli WHERE. Część zapytania przed zastosowaniem operatora GroupBy może być dowolnym złożonym zapytaniem, o ile można je przetłumaczyć na serwer. Ponadto, po zastosowaniu operatorów agregacji w zapytaniu grupującym w celu usunięcia grupowań z wynikowego źródła, można opracowywać na nim zapytania tak jak na każdym innym źródle.
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]
Operatory agregacji obsługiwane przez platformę EF Core są następujące
.SIEĆ | SQL |
---|---|
Średnia(x => x.Własność) | AVG(Właściwość) |
Funkcja Count() | COUNT(*) |
LongCount() | COUNT(*) |
Max(x => x.Property) | MAX(Właściwość) |
Min(x => x.Property) | MIN(Właściwość) |
Sum(x => x.Property) | SUM(Właściwość) |
Dodatkowe operatory agregacji mogą być obsługiwane. Sprawdź dokumenty dostawcy, aby uzyskać więcej mapowań funkcji.
Mimo że nie ma struktury bazy danych reprezentującego IGrouping
, w niektórych przypadkach program EF Core 7.0 i nowsze wersje mogą utworzyć grupowania po zwróceniu wyników z bazy danych. Jest to podobne do działania operatora Include
w przypadku uwzględniania powiązanych kolekcji. Następujące zapytanie LINQ używa operatora GroupBy do grupowania wyników według wartości właściwości Price.
var query = context.Books.GroupBy(s => s.Price);
SELECT [b].[Price], [b].[Id], [b].[AuthorId]
FROM [Books] AS [b]
ORDER BY [b].[Price]
W tym przypadku operator GroupBy nie tłumaczy się bezpośrednio na klauzulę GROUP BY
w języku SQL, ale zamiast tego program EF Core tworzy grupowania po powrocie wyników z serwera.
Łączenie lewostronne
Chociaż lewa zewnętrzna łączność nie jest operatorem LINQ, relacyjne bazy danych mają pojęcie lewej zewnętrznej łączności, która jest często używana w zapytaniach. Określony wzorzec zapytań LINQ daje taki sam wynik jak LEFT JOIN
na serwerze. Program EF Core identyfikuje takie wzorce i generuje odpowiednik LEFT JOIN
po stronie serwera. Wzorzec obejmuje utworzenie metody GroupJoin między źródłami danych, a następnie spłaszczenie grupowania poprzez użycie operatora SelectMany z funkcją DefaultIfEmpty na źródle grupowania, tak by dopasować wartość null, gdy wewnętrzny element nie posiada powiązanego elementu. W poniższym przykładzie pokazano, jak wygląda ten wzorzec i co generuje.
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]
Powyższy wzorzec tworzy złożoną strukturę w drzewie wyrażeń. Z tego powodu, EF Core wymaga, aby rezultaty grupowania operatora GroupJoin zostały spłaszczone w kroku bezpośrednio po zastosowaniu operatora. Nawet jeśliDefaultIfEmpty-SelectMany GroupJoin- jest używany, ale w innym wzorcu, możemy nie zidentyfikować go jako połączenia lewego.