Självstudie: Skriva frågor i C# med hjälp av språkintegrerad fråga (LINQ)

I den här självstudien skapar du en datakälla och skriver flera LINQ-frågor. Du kan experimentera med frågeuttrycken och se skillnaderna i resultaten. Den här genomgången visar de C#-språkfunktioner som används för att skriva LINQ-frågeuttryck. Du kan följa med och skapa appen och experimentera med frågorna själv. Den här artikeln förutsätter att du har installerat den senaste .NET SDK:en. Annars går du till sidan .NET-nedladdningar och installerar den senaste versionen på datorn.

Skapa först programmet. Från konsolen skriver du följande kommando:

dotnet new console -o WalkthroughWritingLinqQueries

Om du föredrar Visual Studio kan du också skapa ett nytt konsolprogram med namnet WalkthroughWritingLinqQueries.

Skapa en minnesintern datakälla

Det första steget är att skapa en datakälla för dina frågor. Datakällan för frågorna är en enkel lista över Student poster. Varje Student post har ett förnamn, ett familjenamn och en matris med heltal som representerar deras testresultat i klassen. Lägg till en ny fil med namnet students.csoch kopiera följande kod till filen:

namespace WalkthroughWritingLinqQueries;

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

Observera följande egenskaper:

  • Posten Student består av automatiskt implementerade egenskaper.
  • Varje elev i listan initieras med den primära konstruktorn.
  • Resultatsekvensen för varje elev initieras med en primär konstruktor.

Skapa sedan en sekvens med Student-poster som fungerar som källa till den här frågan. Öppna Program.csoch ta bort följande exempelkod:

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

Ersätt den här med följande kod som skapar en sekvens med Student-poster:

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])
];
  • Studentsekvensen initieras med ett samlingsuttryck.
  • Den Student posttypen innehåller den statiska listan över alla studenter.
  • Vissa av konstruktoranropen använder namngivna argument för att klargöra vilken argument som matchar med vilken konstruktorparameter.

Prova att lägga till några fler elever med olika testresultat i listan över elever för att bekanta dig mer med koden hittills.

Skapa frågan

Därefter skapar du din första fråga. När du kör frågan skapar du en lista över alla elever vars poäng på det första testet var större än 90. Eftersom hela Student objektet är markerat är frågans typ IEnumerable<Student>. Även om koden också kan använda implicit inmatning med hjälp av nyckelordet var används explicit inmatning för att tydligt illustrera resultat. (Mer information om varfinns i implicit inskrivna lokala variabler.) Lägg till följande kod i Program.cs, efter koden som skapar sekvensen med studenter:

// 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;

Frågans intervallvariabel, student, fungerar som en referens till varje Student i källan, vilket ger medlemsåtkomst för varje objekt.

Kör frågan

Skriv nu den foreach-loop som gör att frågan körs. Varje element i den returnerade sekvensen nås via iterationsvariabeln i foreach-loopen. Typen av den här variabeln är Studentoch frågevariabelns typ är kompatibel, IEnumerable<Student>. När du har lagt till följande kod skapar och kör du programmet för att se resultatet i fönstret Console.

// 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

Om du vill förfina frågan ytterligare kan du kombinera flera booleska villkor i where-satsen. Följande kod lägger till ett villkor så att frågan returnerar de elever vars första poäng var över 90 och vars senaste poäng var mindre än 80. Satsen where bör likna följande kod.

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

Prova föregående where-sats eller experimentera dig själv med andra filtervillkor. Mer information finns i where-satsen.

Beställa frågeresultatet

Det är lättare att skanna resultaten om de är i någon form av ordning. Du kan beställa den returnerade sekvensen efter valfritt tillgängligt fält i källelementen. Följande orderby-sats sorterar resultaten i alfabetisk ordning från A till Z enligt varje elevs efternamn, till exempel. Lägg till följande orderby-sats i frågan direkt efter where-instruktionen och före select-instruktionen:

orderby student.Last ascending

Ändra nu orderby-satsen så att den sorterar resultaten i omvänd ordning enligt poängen på det första testet, från den högsta till den lägsta.

orderby student.Scores[0] descending

Ändra WriteLine formatsträng så att du kan se poängen:

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

Mer information finns i orderby-klausul.

Gruppera resultaten

Gruppering är en kraftfull funktion i frågeuttryck. En fråga med en gruppsats skapar en sekvens med grupper, och varje grupp innehåller en Key och en sekvens som består av alla medlemmar i gruppen. Följande nya fråga grupperar eleverna med hjälp av den första bokstaven i deras familjenamn som nyckel.

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

Typen av fråga har ändrats. Nu skapas en sekvens med grupper som har en char typ som en nyckel och en sekvens med Student objekt. Koden i körningsloopen för foreach måste också ändras:

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

Kör programmet och visa resultatet i fönstret Console. För mer information, se klausul i grupp .

Att uttryckligen koda IEnumerables av IGroupings kan snabbt bli omständligt. Skriv samma fråga och foreach-loop mycket enklare med hjälp av var. Nyckelordet var ändrar inte objekttyperna. det instruerar bara kompilatorn att härleda typerna. Ändra typen av studentQuery och iterationsvariabeln group till var och kör frågan igen. I den inre foreach-loopen skrivs iterationsvariabeln fortfarande som Studentoch frågan fungerar som tidigare. Ändra student iterationsvariabeln till var och kör frågan igen. Du ser att du får exakt samma resultat.

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}");
    }
}

Mer information om varfinns i implicit inskrivna lokala variabler.

Sortera grupperna efter deras nyckelvärde

Grupperna i föregående fråga är inte i alfabetisk ordning. Du kan ange en orderby-sats efter group-satsen. Men om du vill använda en orderby-sats behöver du först en identifierare som fungerar som en referens till de grupper som skapats av group-satsen. Du anger identifieraren med hjälp av nyckelordet into enligt följande:

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

Kör den här frågan och grupperna sorteras nu i alfabetisk ordning.

Du kan använda nyckelordet let för att introducera en identifierare för valfritt uttrycksresultat i frågeuttrycket. Den här identifieraren kan vara en bekvämlighet, som i följande exempel. Det kan också förbättra prestanda genom att lagra resultatet av ett uttryck så att det inte behöver beräknas flera gånger.

// 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

Mer information finns i artikeln om - ochlet-satsen.

Använda metodsyntax i ett frågeuttryck

Enligt beskrivningen i frågesyntax och metodsyntax i LINQkan vissa frågeåtgärder endast uttryckas med hjälp av metodsyntax. Följande kod beräknar totalpoängen för varje Student i källsekvensen och anropar sedan metoden Average() på resultatet av frågan för att beräkna klassens genomsnittliga poäng.

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

Transformera eller projicera i select-satsen

Det är vanligt att en fråga skapar en sekvens vars element skiljer sig från elementen i källsekvenserna. Ta bort eller kommentera ut din tidigare fråga och körningsloop och ersätt den med följande kod. Frågan returnerar en sekvens med strängar (inte Students), och det här faktumet återspeglas i foreach-loopen.

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

Koden tidigare i den här genomgången visade att den genomsnittliga klasspoängen är cirka 334. Om du vill skapa en sekvens med Students vars totala poäng är större än klassgenomsnittet, tillsammans med deras Student ID, kan du använda en anonym typ i select-instruktionen:

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