Udostępnij za pomocą


Samouczek: pisanie zapytań w języku C# przy użyciu zapytania zintegrowanego z językiem (LINQ)

W tym samouczku utworzysz źródło danych i napiszesz kilka zapytań LINQ. Możesz eksperymentować z wyrażeniami zapytań i zobaczyć różnice w wynikach. W tym przewodniku przedstawiono funkcje języka C#, które są używane do pisania wyrażeń zapytań LINQ. Możesz samodzielnie śledzić i kompilować aplikację oraz eksperymentować z zapytaniami. W tym artykule założono, że zainstalowano najnowszy zestaw .NET SDK. Jeśli nie, przejdź do strony Pliki do pobrania platformy .NET i zainstaluj najnowszą wersję na maszynie.

Najpierw utwórz aplikację. W konsoli wpisz następujące polecenie:

dotnet new console -o WalkthroughWritingLinqQueries

Jeśli wolisz program Visual Studio, utwórz nową aplikację konsolową o nazwie WalkthroughWritingLinqQueries.

Tworzenie źródła danych w pamięci

Pierwszym krokiem jest utworzenie źródła danych dla zapytań. Źródło danych dla zapytań to prosta lista rekordów Student . Każdy Student rekord ma imię, nazwę rodziny i tablicę liczb całkowitych reprezentujących wyniki testów w klasie. Dodaj nowy plik o nazwie students.cs i skopiuj następujący kod do tego pliku:

namespace WalkthroughWritingLinqQueries;

public record Student(string First, string Last, int ID, int[] Scores);

Zwróć uwagę na następujące cechy:

  • Rekord Student składa się z automatycznie zaimplementowanych właściwości.
  • Każdy uczeń na liście jest inicjowany przy użyciu konstruktora podstawowego.
  • Sekwencja wyników dla każdego ucznia jest inicjowana przy użyciu konstruktora podstawowego.

Następnie utwórz sekwencję rekordów Student , która służy jako źródło tego zapytania. Otwórz Program.cs i usuń następujący standardowy kod:

// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");

Zastąp go następującym kodem, który tworzy sekwencję rekordów Student :

using WalkthroughWritingLinqQueries;

// Create a data source by using a collection initializer.
IEnumerable<Student> students =
[
    new Student(First: "Svetlana", Last: "Omelchenko", ID: 111, Scores: [97, 92, 81, 60]),
    new Student(First: "Claire",   Last: "O'Donnell",  ID: 112, Scores: [75, 84, 91, 39]),
    new Student(First: "Sven",     Last: "Mortensen",  ID: 113, Scores: [88, 94, 65, 91]),
    new Student(First: "Cesar",    Last: "Garcia",     ID: 114, Scores: [97, 89, 85, 82]),
    new Student(First: "Debra",    Last: "Garcia",     ID: 115, Scores: [35, 72, 91, 70]),
    new Student(First: "Fadi",     Last: "Fakhouri",   ID: 116, Scores: [99, 86, 90, 94]),
    new Student(First: "Hanying",  Last: "Feng",       ID: 117, Scores: [93, 92, 80, 87]),
    new Student(First: "Hugo",     Last: "Garcia",     ID: 118, Scores: [92, 90, 83, 78]),

    new Student("Lance",   "Tucker",      119, [68, 79, 88, 92]),
    new Student("Terry",   "Adams",       120, [99, 82, 81, 79]),
    new Student("Eugene",  "Zabokritski", 121, [96, 85, 91, 60]),
    new Student("Michael", "Tucker",      122, [94, 92, 91, 91])
];
  • Sekwencja uczniów jest inicjowana za pomocą wyrażenia zbioru.
  • Typ Student rekordu zawiera statyczną listę wszystkich uczniów.
  • Niektóre wywołania konstruktora używają nazwanych argumentów , aby wyjaśnić, który argument pasuje do parametru konstruktora.

Spróbuj dodać kilku kolejnych uczniów z różnymi wynikami testu do listy uczniów, aby lepiej zapoznać się z kodem, nad którym pracujesz.

Tworzenie zapytania

Następnie utworzysz pierwsze zapytanie. W wyniku wykonania zapytania zostanie utworzona lista wszystkich uczniów, których wynik w pierwszym teście był większy niż 90. Ponieważ zaznaczono cały Student obiekt, typ zapytania to IEnumerable<Student>. Mimo że kod może również używać niejawnego typowania przy użyciu słowa kluczowego var, jawne typowanie służy do jasnego zilustrowania wyników. (Aby uzyskać więcej informacji na temat var, zobacz Niejawnie wpisane zmienne lokalne.) Dodaj następujący kod do Program.cs, po kodzie, który tworzy sekwencję uczniów:

// Create the query.
// The first line could also be written as "var studentQuery ="
IEnumerable<Student> studentQuery =
    from student in students
    where student.Scores[0] > 90
    select student;

Zmienna zakresu zapytania student służy jako odwołanie do każdego Student w źródle, zapewniając dostęp do składowych każdego obiektu.

Uruchamianie zapytania

Teraz napisz pętlę foreach , która powoduje wykonanie zapytania. Dostęp do każdego elementu w zwróconej sekwencji jest uzyskiwany za pośrednictwem zmiennej foreach iteracji w pętli. Typ tej zmiennej to Student, a typ zmiennej kwerendy jest zgodny, IEnumerable<Student>. Po dodaniu następującego kodu skompiluj i uruchom aplikację, aby wyświetlić wyniki w oknie Konsola .

// Execute the query.
// var could be used here also.
foreach (Student student in studentQuery)
{
    Console.WriteLine($"{student.Last}, {student.First}");
}

// Output:
// Omelchenko, Svetlana
// Garcia, Cesar
// Fakhouri, Fadi
// Feng, Hanying
// Garcia, Hugo
// Adams, Terry
// Zabokritski, Eugene
// Tucker, Michael

Aby dokładniej uściślić zapytanie, możesz połączyć wiele warunków logicznych w klauzuli where . Poniższy kod dodaje warunek, tak aby zapytanie zwróciło tych uczniów, których pierwszy wynik wynosił ponad 90 i którego ostatni wynik był mniejszy niż 80. Klauzula where powinna przypominać następujący kod.

where student.Scores[0] > 90 && student.Scores[3] < 80  

Wypróbuj poprzednią where klauzulę lub poeksperymentuj samodzielnie z innymi warunkami filtrowania. Aby uzyskać więcej informacji, zobacz klauzulę , w której.

Zamawianie wyników zapytania

Łatwiej jest skanować wyniki, jeśli są w jakiejś kolejności. Zwróconą sekwencję można uporządkować według dowolnego dostępnego pola w elementach źródłowych. Na przykład poniższa orderby klauzula porządkuje wyniki w kolejności alfabetycznej od A do Z zgodnie z nazwą rodziny każdego ucznia. Dodaj następującą orderby klauzulę do zapytania bezpośrednio po instrukcji where i przed instrukcją select :

orderby student.Last ascending

Teraz zmień klauzulę orderby tak, aby porządkowała wyniki w odwrotnej kolejności zgodnie z wynikiem w pierwszym teście, od najwyższego wyniku do najniższego wyniku.

orderby student.Scores[0] descending

WriteLine Zmień ciąg formatu, aby zobaczyć wyniki:

Console.WriteLine($"{student.Last}, {student.First} {student.Scores[0]}");

Aby uzyskać więcej informacji, zobacz klauzulę ORDER BY.

Grupuj wyniki

Grupowanie to zaawansowana funkcja w wyrażeniach zapytań. Zapytanie z klauzulą grupy tworzy sekwencję grup, a każda grupa zawiera Key oraz sekwencję składającą się ze wszystkich członków tej grupy. Następujące nowe zapytanie grupuje uczniów przy użyciu pierwszej litery swojego nazwiska rodzinnego jako klucza.

IEnumerable<IGrouping<char, Student>> studentQuery =
    from student in students
    group student by student.Last[0];

Typ zapytania został zmieniony. Teraz tworzy sekwencję grup, które mają char typ jako klucz i sekwencję Student obiektów. Kod w foreach pętli wykonywania musi również ulec zmianie:

foreach (IGrouping<char, Student> studentGroup in studentQuery)
{
    Console.WriteLine(studentGroup.Key);
    foreach (Student student in studentGroup)
    {
        Console.WriteLine($"   {student.Last}, {student.First}");
    }
}
// Output:
// O
//   Omelchenko, Svetlana
//   O'Donnell, Claire
// M
//   Mortensen, Sven
// G
//   Garcia, Cesar
//   Garcia, Debra
//   Garcia, Hugo
// F
//   Fakhouri, Fadi
//   Feng, Hanying
// T
//   Tucker, Lance
//   Tucker, Michael
// A
//   Adams, Terry
// Z
//   Zabokritski, Eugene

Uruchom aplikację i wyświetl wyniki w oknie Konsola . Aby uzyskać więcej informacji, zobacz klauzulę grupy.

Jawne kodowanie IEnumerables z IGroupings może szybko stać się żmudne. Napisz to samo zapytanie i foreach pętlę w sposób znacznie wygodniejszy przy użyciu var. Słowo var kluczowe nie zmienia typów obiektów— po prostu nakazuje kompilatorowi wnioskowanie typów. Zmień typ zmiennej studentQuery i iteracji group na var i uruchom ponownie zapytanie. W pętli wewnętrznej foreach zmienna iteracji jest nadal wpisywana jako Student, a zapytanie działa tak jak poprzednio. Zmień zmienną student iteracji na var i ponownie uruchom zapytanie. Zobaczysz, że uzyskasz dokładnie te same wyniki.

IEnumerable<IGrouping<char, Student>> studentQuery =
    from student in students
    group student by student.Last[0];

foreach (IGrouping<char, Student> studentGroup in studentQuery)
{
    Console.WriteLine(studentGroup.Key);
    foreach (Student student in studentGroup)
    {
        Console.WriteLine($"   {student.Last}, {student.First}");
    }
}

Aby uzyskać więcej informacji na temat var, zobacz Niejawnie typizowane zmienne lokalne.

Porządkowanie grup według ich wartości klucza

Grupy w poprzednim zapytaniu nie są w kolejności alfabetycznej. Klauzulę orderby można podać po klauzuli group . Jednak aby użyć orderby klauzuli, najpierw potrzebujesz identyfikatora, który służy jako odwołanie do grup utworzonych przez klauzulę group . Identyfikator należy podać przy użyciu słowa kluczowego into w następujący sposób:

var studentQuery4 =
    from student in students
    group student by student.Last[0] into studentGroup
    orderby studentGroup.Key
    select studentGroup;

foreach (var groupOfStudents in studentQuery4)
{
    Console.WriteLine(groupOfStudents.Key);
    foreach (var student in groupOfStudents)
    {
        Console.WriteLine($"   {student.Last}, {student.First}");
    }
}

// Output:
//A
//   Adams, Terry
//F
//   Fakhouri, Fadi
//   Feng, Hanying
//G
//   Garcia, Cesar
//   Garcia, Debra
//   Garcia, Hugo
//M
//   Mortensen, Sven
//O
//   Omelchenko, Svetlana
//   O'Donnell, Claire
//T
//   Tucker, Lance
//   Tucker, Michael
//Z
//   Zabokritski, Eugene

Uruchom to zapytanie, a grupy są teraz sortowane w kolejności alfabetycznej.

Możesz użyć słowa kluczowego let, żeby wprowadzić identyfikator dla dowolnego wyniku wyrażenia w wyrażeniu zapytania. Ten identyfikator może być wygodą, jak w poniższym przykładzie. Może również zwiększyć wydajność, przechowując wyniki wyrażenia, aby nie trzeba było ich wielokrotnie obliczać.

// This query returns those students whose
// first test score was higher than their
// average score.
var studentQuery5 =
    from student in students
    let totalScore = student.Scores[0] + student.Scores[1] +
        student.Scores[2] + student.Scores[3]
    where totalScore / 4 < student.Scores[0]
    select $"{student.Last}, {student.First}";

foreach (string s in studentQuery5)
{
    Console.WriteLine(s);
}

// Output:
// Omelchenko, Svetlana
// O'Donnell, Claire
// Mortensen, Sven
// Garcia, Cesar
// Fakhouri, Fadi
// Feng, Hanying
// Garcia, Hugo
// Adams, Terry
// Zabokritski, Eugene
// Tucker, Michael

Aby uzyskać więcej informacji, zobacz artykuł na temat klauzulilet .

Używanie składni metody w wyrażeniu zapytania

Zgodnie z opisem w artykule Składnia zapytań i składnia metody w LINQ niektóre operacje zapytań można wyrazić tylko przy użyciu składni metody. Poniższy kod oblicza łączny wynik dla każdej Student w sekwencji źródłowej, a następnie wywołuje metodę Average() na wynikach tego zapytania, aby obliczyć średni wynik klasy.

var studentQuery =
    from student in students
    let totalScore = student.Scores[0] + student.Scores[1] +
        student.Scores[2] + student.Scores[3]
    select totalScore;

double averageScore = studentQuery.Average();
Console.WriteLine($"Class average score = {averageScore}");

// Output:
// Class average score = 334.166666666667

Aby przekształcić lub rzutować w klauzuli select

Często zapytanie tworzy sekwencję, której elementy różnią się od elementów w sekwencjach źródłowych. Usuń lub oznacz jako komentarz swoje poprzednie zapytanie i pętlę wykonawczą, a następnie zastąp je następującym kodem. Zapytanie zwraca sekwencję ciągów (nie Students), a ten fakt jest odzwierciedlany w foreach pętli.

IEnumerable<string> studentQuery =
    from student in students
    where student.Last == "Garcia"
    select student.First;

Console.WriteLine("The Garcias in the class are:");
foreach (string s in studentQuery)
{
    Console.WriteLine(s);
}

// Output:
// The Garcias in the class are:
// Cesar
// Debra
// Hugo

Jak wskazano wcześniej w tym przewodniku, kod pokazał, że średni wynik klasy wynosi około 334. Aby utworzyć sekwencję Students, której łączna ocena jest większa niż średnia klasy, wraz z ich Student ID, można użyć typu anonimowego w instrukcji select.

var aboveAverageQuery =
    from student in students
    let x = student.Scores[0] + student.Scores[1] +
        student.Scores[2] + student.Scores[3]
    where x > averageScore
    select new { id = student.ID, score = x };

foreach (var item in aboveAverageQuery)
{
    Console.WriteLine("Student ID: {0}, Score: {1}", item.id, item.score);
}

// Output:
// Student ID: 113, Score: 338
// Student ID: 114, Score: 353
// Student ID: 116, Score: 369
// Student ID: 117, Score: 352
// Student ID: 118, Score: 343
// Student ID: 120, Score: 341
// Student ID: 122, Score: 368