1. Wprowadzenie
W świecie LINQ są dwie główne "szkoły" pisania zapytań, dwa style, które na pierwszy rzut oka mogą wydawać się zupełnie różne. To Query Syntax (składnia zapytań) i Method Syntax (składnia metod). I jeśli kiedykolwiek myślałeś, że programowanie to tylko logika, to dziś dodamy trochę sztuki, bo wybór składni często przypomina wybór między pędzlem a ołówkiem: oba narzędzia rysują, ale jedno może lepiej oddać kolor, a drugie — kontur.
Po co nam dwie składnie? Dobre pytanie! Wyobraź sobie, że chcesz zamówić kawę. Możesz powiedzieć: "Czy mogę prosić filiżankę espresso z mlekiem i jedną łyżeczką cukru?". A możesz napisać na kartce: "Espresso. Mleko. 1 cukier". Obie opcje są zrozumiałe, ale jedna jest bardziej rozmowna, druga — bardziej zwięzła.
Tak samo jest z LINQ. Jedna składnia została stworzona, by być maksymalnie podobną do języka baz danych SQL, a druga — by być bardziej "C#-owa" i elastyczna. Ostatecznie kompilator C# zamienia oba style na to samo: wywołania metod. Więc wybór często sprowadza się do czytelności i osobistych preferencji.
Zobaczmy oba style.
2. Method Syntax
To wariant, w którym wywołujesz metody rozszerzające LINQ jedna po drugiej przez kropkę. Takie podejście jest szczególnie popularne wśród .NET-deweloperów, bo łatwo dodawać nowe operacje, wynik zawsze jest IEnumerable<T>, i wygodnie się pisze bezpośrednio w kodzie.
Przykład: Filtrowanie i sortowanie produktów
var filteredProducts = products
.Where(p => p.Price < 1000) // Filtrowanie po cenie
.OrderBy(p => p.Name) // Sortowanie po nazwie
.ToList(); // Konwersja do List<Product>
Where, OrderBy i ToList — to metody rozszerzające LINQ, każda zwraca nowy zbiór danych, który można dalej przetwarzać.
Schemat
graph LR
A[products] --> B[Where]
B --> C[OrderBy]
C --> D[ToList]
Dlaczego Method Syntax jest fajny
- Bardzo elastyczny: łatwo budować złożone łańcuchy wielu operacji.
- Wszystko to zwykłe metody C#, IDE pomaga z podpowiedziami.
- Pozwala robić prawie wszystko, co jest w LINQ (a nawet więcej).
Kolejny przykład: wybór imion wszystkich użytkowników powyżej 18 lat
Załóżmy, że mamy klasę User:
public class User
{
public string Name { get; set; }
public int Age { get; set; }
}
Teraz wybierzemy imiona dorosłych użytkowników:
List<User> users = ... // gdzieś zadeklarowane
var adultNames = users
.Where(u => u.Age >= 18)
.Select(u => u.Name)
.ToList();
3. Query Syntax (składnia podobna do SQL)
Ta składnia została specjalnie wymyślona, żeby deweloperzy C# czuli się jak w domu, jeśli wcześniej pracowali z SQL. Przypomina zapytania do bazy danych i zaczyna się od słowa kluczowego from.
Przykład: ten sam filtr i sortowanie
var filteredProducts =
from p in products
where p.Price < 1000
orderby p.Name
select p;
Tutaj wszystko czyta się jak klasyczne zapytanie: "z kolekcji products weź p, gdzie p.Price < 1000, posortuj po p.Name, wybierz p".
Schemat
flowchart TD
A[products] -->|from p in products| B[where p.Price < 1000]
B --> C[orderby p.Name]
C --> D[select p]
Dlaczego Query Syntax jest fajny
- Przypomina SQL, więc jest bardziej zrozumiały dla tych, którzy już pracowali z bazami danych.
- Bardziej czytelny dla długich zapytań z wieloma warunkami, grupowaniem, łączeniem (join).
Kolejny przykład: wybór imion dorosłych użytkowników
var adultNames =
from u in users
where u.Age >= 18
select u.Name;
Zwróć uwagę: Tutaj od razu po select możesz wybrać nie cały obiekt, a konkretne pole — na przykład tylko imię.
4. Porównanie: Method Syntax vs Query Syntax
| Method Syntax | Query Syntax | |
|---|---|---|
| Składnia | |
|
| Przypomina | Zwykłe metody/łańcuchy | SQL |
| Gdzie używać | Zawsze, do dowolnych operacji | Nie wszystkie operacje dostępne (np. , tylko w Method Syntax) |
| Czytelność | Dobre dla łańcuchów | Wygodniejsze dla grupowania, łączenia |
| Co zwraca | Zwykle |
Zwykle , ale czasem trzeba dla listy |
Ciekawostka z życia
Większość przykładów w oficjalnej dokumentacji Microsoft do LINQ jest podana od razu w obu stylach. Ale w kodzie produkcyjnym coraz częściej spotyka się Method Syntax — jest wygodniejszy przy integracji z metodami rozszerzającymi (Where, Select, OrderBy i tak dalej).
Mieszać czy nie mieszać? Jaki styl wybrać
Można swobodnie łączyć oba style w jednym projekcie (a nawet w jednym zapytaniu — ale wygląda to dziwnie). Najważniejsze — nie robić z kodu sałatki. Zwykle wybiera się jeden styl na cały moduł lub projekt, żeby kod był łatwiejszy do czytania.
Method Syntax świetnie nadaje się do sekwencyjnych przekształceń i używania wszystkich metod LINQ, które są tylko jako metody (Sum, Count, Any i kilka innych).
Query Syntax jest bardzo wygodny do zadań z join, group by albo złożonymi wielopoziomowymi warunkami — tam wygrywa czytelnością.
5. Przekształcanie między stylami: to samo, ale inaczej
LINQ w C# zamienia każde zapytanie podobne do SQL (Query Syntax) na łańcuch wywołań metod (Method Syntax). Czyli, cokolwiek napiszesz, C# i tak sprowadzi to do metod rozszerzających przy kompilacji.
Przykład 1 — filtrowanie:
Query Syntax:
var adults = from u in users
where u.Age >= 18
select u;
Method Syntax (odpowiednik):
var adults = users.Where(u => u.Age >= 18);
Przykład 2 — wybieranie pól (Select):
Query Syntax:
var names = from u in users
select u.Name;
Method Syntax:
var names = users.Select(u => u.Name);
7. Grupowanie i łączenie (join, group by)
Tworzenie nowych obiektów
Dodajmy do naszej aplikacji edukacyjnej możliwość wyświetlania listy użytkowników pogrupowanych według wieku. Tutaj różnica w składni staje się szczególnie widoczna.
Query Syntax (grupowanie):
var usersByAge =
from u in users
group u by u.Age into ageGroup
select new { Age = ageGroup.Key, Users = ageGroup.ToList() };
Grupujemy użytkowników według wieku (group u by u.Age). Po into pojawia się zmienna ageGroup — to już sama grupa.
Method Syntax (odpowiednik):
var usersByAge = users
.GroupBy(u => u.Age)
.Select(ageGroup => new { Age = ageGroup.Key, Users = ageGroup.ToList() });
Jeszcze trudniej: łączenie (join)
Załóżmy, że mamy listę zamówień (orders) i listę użytkowników (users). Chcemy uzyskać imiona użytkowników i sumę ich zamówień.
Query Syntax:
var userOrders =
from user in users
join order in orders on user.Id equals order.UserId
select new { user.Name, order.Amount };
Method Syntax:
var userOrders = users.Join(
orders,
user => user.Id,
order => order.UserId,
(user, order) => new { user.Name, order.Amount }
);
8. Częste błędy, pułapki i lifehacki
Jedno z najczęstszych nieporozumień: jeśli nie dodasz .ToList(), wynik — to nie lista, tylko "leniwe zapytanie" (lazy query), które wykona się dopiero przy pierwszym przejściu po kolekcji. To wygodne, ale może prowadzić do nieoczekiwanych rezultatów, jeśli oryginalna kolekcja zmieni się po napisaniu zapytania. Więcej o "leniwej ewaluacji" pogadamy osobno (po poznaniu metod LINQ).
Wielu początkujących myli się, że można użyć wszystkich metod LINQ w Query Syntax. Na przykład nie da się po prostu napisać select sum(u.Age), trzeba przejść do Method Syntax (users.Sum(u => u.Age)).
GO TO FULL VERSION