Standardowe operatory zapytań — omówienie
Standardowe operatory zapytań to słowa kluczowe i metody, które tworzą wzorzec LINQ. Język C# definiuje słów kluczowych zapytań LINQ używanych do najbardziej typowego wyrażenia zapytania. Kompilator tłumaczy wyrażenia przy użyciu tych słów kluczowych do równoważnych wywołań metody. Te dwie formy są synonimami. Inne metody, które są częścią przestrzeni nazw System.Linq, nie mają równoważnych słów kluczowych zapytania. W takich przypadkach należy używać składni metod. W tej sekcji omówiono wszystkie słowa kluczowe operatora zapytania. Środowisko uruchomieniowe i inne pakiety NuGet dodają więcej metod zaprojektowanych do pracy z zapytaniami LINQ w każdej wersji. Najbardziej typowe metody, w tym te, które mają odpowiedniki słów kluczowych zapytania, zostały omówione w tej sekcji. Pełną listę metod zapytań obsługiwanych przez środowisko uruchomieniowe platformy .NET można znaleźć w dokumentacji interfejsu API System.Linq.Enumerable. Oprócz metod omówionych tutaj ta klasa zawiera metody łączenia źródeł danych, obliczania pojedynczej wartości ze źródła danych, takich jak suma, średnia lub inna wartość.
Ważne
Te przykłady korzystają ze źródła danych System.Collections.Generic.IEnumerable<T>. Źródła danych oparte na System.Linq.IQueryProvider używają źródeł danych System.Linq.IQueryable<T> i drzew wyrażeń . Drzewa wyrażeń mają ograniczenia w dozwolonej składni języka C#. Ponadto każde źródło danych IQueryProvider
, takie jak EF Core, może nakładać więcej ograniczeń. Zapoznaj się z dokumentacją źródła danych.
Większość z tych metod działa na sekwencjach, gdzie sekwencja jest obiektem, którego typ implementuje interfejs IEnumerable<T> lub interfejs IQueryable<T>. Standardowe operatory zapytań zapewniają możliwości zapytań, w tym filtrowanie, projekcję, agregację, sortowanie i nie tylko. Metody, które składają się na każdy zestaw, to statyczni członkowie klas Enumerable i Queryable. Są one definiowane jako metody rozszerzenia typu, na którym działają.
Rozróżnienie między sekwencjami IEnumerable<T> i IQueryable<T> określa sposób wykonywania zapytania w czasie wykonywania.
W przypadku IEnumerable<T>
zwrócony obiekt wyliczalny przechwytuje argumenty przekazane do metody. Gdy ten obiekt zostanie wyliczony, zostanie zastosowana logika operatora zapytania, a wyniki zapytania zostaną zwrócone.
W przypadku IQueryable<T>
zapytanie jest tłumaczone na drzewo wyrażeń . Drzewo wyrażeń można przetłumaczyć na zapytanie natywne, gdy źródło danych może zoptymalizować zapytanie. Biblioteki, takie jak Entity Framework, tłumaczą zapytania LINQ na natywne zapytania SQL, które są wykonywane w bazie danych.
Poniższy przykład kodu pokazuje, jak standardowe operatory zapytań mogą służyć do uzyskiwania informacji o sekwencji.
string sentence = "the quick brown fox jumps over the lazy dog";
// Split the string into individual words to create a collection.
string[] words = sentence.Split(' ');
// Using query expression syntax.
var query = from word in words
group word.ToUpper() by word.Length into gr
orderby gr.Key
select new { Length = gr.Key, Words = gr };
// Using method-based query syntax.
var query2 = words.
GroupBy(w => w.Length, w => w.ToUpper()).
Select(g => new { Length = g.Key, Words = g }).
OrderBy(o => o.Length);
foreach (var obj in query)
{
Console.WriteLine($"Words of length {obj.Length}:");
foreach (string word in obj.Words)
Console.WriteLine(word);
}
// This code example produces the following output:
//
// Words of length 3:
// THE
// FOX
// THE
// DOG
// Words of length 4:
// OVER
// LAZY
// Words of length 5:
// QUICK
// BROWN
// JUMPS
Jeśli to możliwe, zapytania w tej sekcji używają sekwencji słów lub liczb jako źródła danych wejściowych. W przypadku zapytań, w których są używane bardziej skomplikowane relacje między obiektami, używane są następujące źródła, które modelują szkołę:
public enum GradeLevel
{
FirstYear = 1,
SecondYear,
ThirdYear,
FourthYear
};
public class Student
{
public required string FirstName { get; init; }
public required string LastName { get; init; }
public required int ID { get; init; }
public required GradeLevel Year { get; init; }
public required List<int> Scores { get; init; }
public required int DepartmentID { get; init; }
}
public class Teacher
{
public required string First { get; init; }
public required string Last { get; init; }
public required int ID { get; init; }
public required string City { get; init; }
}
public class Department
{
public required string Name { get; init; }
public int ID { get; init; }
public required int TeacherID { get; init; }
}
Każda Student
ma poziom klasy, dział podstawowy i serię wyników. Obiekt Teacher
ma również właściwość City
, która identyfikuje kampus, w którym nauczyciel prowadzi zajęcia.
Department
ma nazwę i odniesienie do Teacher
, który służy jako szef działu.
Zestaw danych można znaleźć w repozytorium źródłowym .
Typy operatorów zapytań
Standardowe operatory zapytań różnią się czasem wykonywania w zależności od tego, czy zwracają pojedynczą wartość, czy sekwencję wartości. Metody zwracające pojedynczą wartość (na przykład Average i Sum) są wykonywane natychmiast. Metody, które zwracają sekwencję, odkładają wykonanie zapytania i zwracają obiekt wyliczalny. Możesz użyć sekwencji danych wyjściowych jednego zapytania jako sekwencji danych wejściowych do innego zapytania. Wywołania metod zapytań można połączyć w łańcuch w jednym zapytaniu, co umożliwia zapytania o dowolnym stopniu złożoności.
Operatory zapytań
W zapytaniu LINQ pierwszym krokiem jest określenie źródła danych. W zapytaniu LINQ klauzula from
występuje jako pierwsza, aby wprowadzić źródło danych (students
) i zmienną zakresu (student
).
//queryAllStudents is an IEnumerable<Student>
var queryAllStudents = from student in students
select student;
Zmienna zakresu jest podobna do zmiennej iteracji w pętli foreach
, z tą różnicą, że w wyrażeniu zapytania nie ma rzeczywistej iteracji. Po wykonaniu zapytania zmienna zakresu służy jako odwołanie do każdego kolejnego elementu w students
. Ponieważ kompilator może wywnioskować typ student
, nie trzeba jawnie go określać. W klauzuli let
można wprowadzić więcej zmiennych zakresu. Aby uzyskać więcej informacji, zobacz klauzulę let .
Uwaga
W przypadku źródeł danych innych niż ogólne, takich jak ArrayList, zmienna zakresu musi być jawnie wpisana. Aby uzyskać więcej informacji, zobacz How to query an ArrayList with LINQ (C#) and from clause.
Po uzyskaniu źródła danych można wykonać dowolną liczbę operacji na tym źródle danych:
-
Filtruj dane przy użyciu słowa kluczowego
where
. -
uporządkuj dane przy użyciu
orderby
i opcjonalniedescending
słów kluczowych. -
Grupuj dane, używając
group
i opcjonalnieinto
jako słów kluczowych. -
dołącz dane przy użyciu słowa kluczowego
join
. -
dane programu Project przy użyciu słowa kluczowego
select
.
Tabela składni wyrażeń zapytań
W poniższej tabeli wymieniono standardowe operatory zapytań, które mają równoważne klauzule wyrażenia zapytania.
Metoda | Składnia wyrażeń zapytań języka C# |
---|---|
Cast | Użyj jawnie typizowanej zmiennej zakresu:from int i in numbers (Aby uzyskać więcej informacji, zobacz z klauzuli.) |
GroupBy | group … by — lub — group … by … into … (Aby uzyskać więcej informacji, zobacz klauzulę grupy .) |
GroupJoin<TOuter,TInner,TKey,TResult>(IEnumerable<TOuter>, IEnumerable<TInner>, Func<TOuter,TKey>, Func<TInner,TKey>, Func<TOuter,IEnumerable<TInner>, TResult>) | join … in … on … equals … into … (Aby uzyskać więcej informacji, zobacz klauzulę połączenia .) |
Join<TOuter,TInner,TKey,TResult>(IEnumerable<TOuter>, IEnumerable<TInner>, Func<TOuter,TKey>, Func<TInner,TKey>, Func<TOuter,TInner,TResult>) | join … in … on … equals … (Aby uzyskać więcej informacji, zobacz klauzulę join .) |
OrderBy<TSource,TKey>(IEnumerable<TSource>, Func<TSource,TKey>) | orderby (Aby uzyskać więcej informacji, zobacz klauzula orderby.) |
OrderByDescending<TSource,TKey>(IEnumerable<TSource>, Func<TSource,TKey>) | orderby … descending (Aby uzyskać więcej informacji, zobacz klauzulę sortowania .) |
Select | select (Aby uzyskać więcej informacji, zobacz klauzulę SELECT .) |
SelectMany | Wiele from klauzul.(Aby uzyskać więcej informacji, zobacz z klauzuli.) |
ThenBy<TSource,TKey>(IOrderedEnumerable<TSource>, Func<TSource,TKey>) | orderby …, … (Aby uzyskać więcej informacji, zobacz klauzulę ORDER BY .) |
ThenByDescending<TSource,TKey>(IOrderedEnumerable<TSource>, Func<TSource,TKey>) | orderby …, … descending (Aby uzyskać więcej informacji, zobacz klauzulę "orderby" .) |
Where | where (Aby uzyskać więcej informacji, zobacz where clause.) |
Przekształcenia danych za pomocą LINQ
Language-Integrated Query (LINQ) nie dotyczy tylko pobierania danych. Jest to również zaawansowane narzędzie do przekształcania danych. Za pomocą zapytania LINQ można użyć sekwencji źródłowej jako danych wejściowych i zmodyfikować ją na wiele sposobów, aby utworzyć nową sekwencję danych wyjściowych. Sekwencję można modyfikować bez modyfikowania samych elementów przez sortowanie i grupowanie. Ale być może najbardziej zaawansowaną funkcją zapytań LINQ jest możliwość tworzenia nowych typów. Klauzula select tworzy element wyjściowy z elementu wejściowego. Służy do przekształcania elementu wejściowego w element wyjściowy:
- Połącz wiele sekwencji wejściowych w jedną sekwencję wyjściową, która ma nowy typ.
- Utwórz sekwencje wyjściowe, których elementy składają się tylko z jednej lub kilku właściwości każdego elementu w sekwencji źródłowej.
- Utwórz sekwencje wyjściowe, których elementy składają się z wyników operacji wykonywanych na danych źródłowych.
- Tworzenie sekwencji danych wyjściowych w innym formacie. Można na przykład przekształcić dane z wierszy SQL lub plików tekstowych w format XML.
Te przekształcenia można łączyć na różne sposoby w tym samym zapytaniu. Ponadto sekwencja wyjściowa jednego zapytania może służyć jako sekwencja wejściowa dla nowego zapytania. Poniższy przykład przekształca obiekty w strukturze danych w pamięci na elementy XML.
// Create the query.
var studentsToXML = new XElement("Root",
from student in students
let scores = string.Join(",", student.Scores)
select new XElement("student",
new XElement("First", student.FirstName),
new XElement("Last", student.LastName),
new XElement("Scores", scores)
) // end "student"
); // end "Root"
// Execute the query.
Console.WriteLine(studentsToXML);
Kod generuje następujące dane wyjściowe XML:
<Root>
<student>
<First>Svetlana</First>
<Last>Omelchenko</Last>
<Scores>97,90,73,54</Scores>
</student>
<student>
<First>Claire</First>
<Last>O'Donnell</Last>
<Scores>56,78,95,95</Scores>
</student>
...
<student>
<First>Max</First>
<Last>Lindgren</Last>
<Scores>86,88,96,63</Scores>
</student>
<student>
<First>Arina</First>
<Last>Ivanova</Last>
<Scores>93,63,70,80</Scores>
</student>
</Root>
Aby uzyskać więcej informacji, zobacz Tworzenie drzew XML w języku C# (LINQ to XML).
Możesz użyć wyników jednego zapytania jako źródła danych dla kolejnego zapytania. W tym przykładzie pokazano, jak uporządkować wyniki operacji sprzężenia. To zapytanie tworzy połączenie grup, a następnie sortuje te grupy na podstawie elementu kategorii, który jest nadal w kontekście. Wewnątrz inicjatora typu anonimowego podzapytywanie porządkuje wszystkie pasujące elementy z sekwencji produktów.
var orderedQuery = from department in departments
join student in students on department.ID equals student.DepartmentID into studentGroup
orderby department.Name
select new
{
DepartmentName = department.Name,
Students = from student in studentGroup
orderby student.LastName
select student
};
foreach (var departmentList in orderedQuery)
{
Console.WriteLine(departmentList.DepartmentName);
foreach (var student in departmentList.Students)
{
Console.WriteLine($" {student.LastName,-10} {student.FirstName,-10}");
}
}
/* Output:
Chemistry
Balzan Josephine
Fakhouri Fadi
Popov Innocenty
Seleznyova Sofiya
Vella Carmen
Economics
Adams Terry
Adaobi Izuchukwu
Berggren Jeanette
Garcia Cesar
Ifeoma Nwanneka
Jamuike Ifeanacho
Larsson Naima
Svensson Noel
Ugomma Ifunanya
Engineering
Axelsson Erik
Berg Veronika
Engström Nancy
Hicks Cassie
Keever Bruce
Micallef Nicholas
Mortensen Sven
Nilsson Erna
Tucker Michael
Yermolayeva Anna
English
Andersson Sarah
Feng Hanying
Ivanova Arina
Jakobsson Jesper
Jensen Christiane
Johansson Mark
Kolpakova Nadezhda
Omelchenko Svetlana
Urquhart Donald
Mathematics
Frost Gaby
Garcia Hugo
Hedlund Anna
Kovaleva Katerina
Lindgren Max
Maslova Evgeniya
Olsson Ruth
Sammut Maria
Sazonova Anastasiya
Physics
Åkesson Sami
Edwards Amy E.
Falzon John
Garcia Debra
Hansson Sanna
Mattsson Martina
Richardson Don
Zabokritski Eugene
*/
Równoważne zapytanie używające składni metody jest wyświetlane w następującym kodzie:
var orderedQuery = departments
.GroupJoin(students, department => department.ID, student => student.DepartmentID,
(department, studentGroup) => new
{
DepartmentName = department.Name,
Students = studentGroup.OrderBy(student => student.LastName)
})
.OrderBy(department => department.DepartmentName);
foreach (var departmentList in orderedQuery)
{
Console.WriteLine(departmentList.DepartmentName);
foreach (var student in departmentList.Students)
{
Console.WriteLine($" {student.LastName,-10} {student.FirstName,-10}");
}
}
Chociaż można użyć klauzuli orderby
z jedną lub więcej sekwencjami źródłowymi przed sprzężeniem, ogólnie tego nie zalecamy. Niektórzy dostawcy LINQ mogą nie zachować tej kolejności po sprzężeniu. Aby uzyskać więcej informacji, zobacz klauzulę łączenia .
Zobacz też
- Enumerable
- Queryable
- klauzula SELECT
- metody rozszerzenia
- Słowa kluczowe języka zapytań (LINQ)
- Typy anonimowe