This walkthrough demonstrates the C# language features that are used to write LINQ query expressions.
Create a C# Project
Note
The following instructions are for Visual Studio. If you are using a different development environment, create a console project with a reference to System.Core.dll and a using directive for the System.Linq namespace.
To create a project in Visual Studio
Start Visual Studio.
On the menu bar, choose File, New, Project.
The New Project dialog box opens.
Expand Installed, expand Templates, expand Visual C#, and then choose Console Application.
In the Name text box, enter a different name or accept the default name, and then choose the OK button.
The new project appears in Solution Explorer.
Notice that your project has a reference to System.Core.dll and a
usingdirective for the System.Linq namespace.
Create an in-Memory Data Source
The data source for the queries is a simple list of Student objects. Each Student record has a first name, last name, and an array of integers that represents their test scores in the class. Copy this code into your project. Note the following characteristics:
The
Studentclass consists of auto-implemented properties.Each student in the list is initialized with an object initializer.
The list itself is initialized with a collection initializer.
This whole data structure will be initialized and instantiated without explicit calls to any constructor or explicit member access. For more information about these new features, see Auto-Implemented Properties and Object and Collection Initializers.
To add the data source
Add the
Studentclass and the initialized list of students to theProgramclass in your project.public class Student { public string First { get; set; } public string Last { get; set; } public int ID { get; set; } public List<int> Scores; } // Create a data source by using a collection initializer. static List<Student> students = new List<Student> { new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores= new List<int> {97, 92, 81, 60}}, new Student {First="Claire", Last="O'Donnell", ID=112, Scores= new List<int> {75, 84, 91, 39}}, new Student {First="Sven", Last="Mortensen", ID=113, Scores= new List<int> {88, 94, 65, 91}}, new Student {First="Cesar", Last="Garcia", ID=114, Scores= new List<int> {97, 89, 85, 82}}, new Student {First="Debra", Last="Garcia", ID=115, Scores= new List<int> {35, 72, 91, 70}}, new Student {First="Fadi", Last="Fakhouri", ID=116, Scores= new List<int> {99, 86, 90, 94}}, new Student {First="Hanying", Last="Feng", ID=117, Scores= new List<int> {93, 92, 80, 87}}, new Student {First="Hugo", Last="Garcia", ID=118, Scores= new List<int> {92, 90, 83, 78}}, new Student {First="Lance", Last="Tucker", ID=119, Scores= new List<int> {68, 79, 88, 92}}, new Student {First="Terry", Last="Adams", ID=120, Scores= new List<int> {99, 82, 81, 79}}, new Student {First="Eugene", Last="Zabokritski", ID=121, Scores= new List<int> {96, 85, 91, 60}}, new Student {First="Michael", Last="Tucker", ID=122, Scores= new List<int> {94, 92, 91, 91} } };
To add a new Student to the Students list
- Add a new
Studentto theStudentslist and use a name and test scores of your choice. Try typing all the new student information in order to better learn the syntax for the object initializer.
Create the Query
To create a simple query
In the application's
Mainmethod, create a simple query that, when it is executed, will produce a list of all students whose score on the first test was greater than 90. Note that because the wholeStudentobject is selected, the type of the query isIEnumerable<Student>. Although the code could also use implicit typing by using the var keyword, explicit typing is used to clearly illustrate results. (For more information aboutvar, see Implicitly Typed Local Variables.)Note also that the query's range variable,
student, serves as a reference to eachStudentin the source, providing member access for each object.
// 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;
Execute the Query
To execute the query
Now write the
foreachloop that will cause the query to execute. Note the following about the code:Each element in the returned sequence is accessed through the iteration variable in the
foreachloop.The type of this variable is
Student, and the type of the query variable is compatible,IEnumerable<Student>.
After you have added this code, build and run the application to see the results in the Console window.
// Execute the query.
// var could be used here also.
foreach (Student student in studentQuery)
{
Console.WriteLine("{0}, {1}", student.Last, student.First);
}
// Output:
// Omelchenko, Svetlana
// Garcia, Cesar
// Fakhouri, Fadi
// Feng, Hanying
// Garcia, Hugo
// Adams, Terry
// Zabokritski, Eugene
// Tucker, Michael
To add another filter condition
You can combine multiple Boolean conditions in the
whereclause in order to further refine a query. The following code adds a condition so that the query returns those students whose first score was over 90 and whose last score was less than 80. Thewhereclause should resemble the following code.where student.Scores[0] > 90 && student.Scores[3] < 80For more information, see where clause.
Modify the Query
To order the results
It will be easier to scan the results if they are in some kind of order. You can order the returned sequence by any accessible field in the source elements. For example, the following
orderbyclause orders the results in alphabetical order from A to Z according to the last name of each student. Add the followingorderbyclause to your query, right after thewherestatement and before theselectstatement:orderby student.Last ascendingNow change the
orderbyclause so that it orders the results in reverse order according to the score on the first test, from the highest score to the lowest score.orderby student.Scores[0] descendingChange the
WriteLineformat string so that you can see the scores:Console.WriteLine("{0}, {1} {2}", student.Last, student.First, student.Scores[0]);For more information, see orderby clause.
To group the results
Grouping is a powerful capability in query expressions. A query with a group clause produces a sequence of groups, and each group itself contains a
Keyand a sequence that consists of all the members of that group. The following new query groups the students by using the first letter of their last name as the key.// studentQuery2 is an IEnumerable<IGrouping<char, Student>> var studentQuery2 = from student in students group student by student.Last[0];Note that the type of the query has now changed. It now produces a sequence of groups that have a
chartype as a key, and a sequence ofStudentobjects. Because the type of the query has changed, the following code changes theforeachexecution loop also:// studentGroup is a IGrouping<char, Student> foreach (var studentGroup in studentQuery2) { Console.WriteLine(studentGroup.Key); foreach (Student student in studentGroup) { Console.WriteLine(" {0}, {1}", 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, EugeneRun the application and view the results in the Console window.
For more information, see group clause.
To make the variables implicitly typed
Explicitly coding
IEnumerablesofIGroupingscan quickly become tedious. You can write the same query andforeachloop much more conveniently by usingvar. Thevarkeyword does not change the types of your objects; it just instructs the compiler to infer the types. Change the type ofstudentQueryand the iteration variablegrouptovarand rerun the query. Note that in the innerforeachloop, the iteration variable is still typed asStudent, and the query works just as before. Change thesiteration variable tovarand run the query again. You see that you get exactly the same results.var studentQuery3 = from student in students group student by student.Last[0]; foreach (var groupOfStudents in studentQuery3) { Console.WriteLine(groupOfStudents.Key); foreach (var student in groupOfStudents) { Console.WriteLine(" {0}, {1}", 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, EugeneFor more information about var, see Implicitly Typed Local Variables.
To order the groups by their key value
When you run the previous query, you notice that the groups are not in alphabetical order. To change this, you must provide an
orderbyclause after thegroupclause. But to use anorderbyclause, you first need an identifier that serves as a reference to the groups created by thegroupclause. You provide the identifier by using theintokeyword, as follows: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(" {0}, {1}", 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, EugeneWhen you run this query, you will see the groups are now sorted in alphabetical order.
To introduce an identifier by using let
You can use the
letkeyword to introduce an identifier for any expression result in the query expression. This identifier can be a convenience, as in the following example, or it can enhance performance by storing the results of an expression so that it does not have to be calculated multiple times.// studentQuery5 is an IEnumerable<string> // 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 MichaelFor more information, see let clause.
To use method syntax in a query expression
As described in Query Syntax and Method Syntax in LINQ, some query operations can only be expressed by using method syntax. The following code calculates the total score for each
Studentin the source sequence, and then calls theAverage()method on the results of that query to calculate the average score of the class. Note the placement of parentheses around the query expression.var studentQuery6 = from student in students let totalScore = student.Scores[0] + student.Scores[1] + student.Scores[2] + student.Scores[3] select totalScore; double averageScore = studentQuery6.Average(); Console.WriteLine("Class average score = {0}", averageScore); // Output: // Class average score = 334.166666666667
To transform or project in the select clause
It is very common for a query to produce a sequence whose elements differ from the elements in the source sequences. Delete or comment out your previous query and execution loop, and replace it with the following code. Note that the query returns a sequence of strings (not
Students), and this fact is reflected in theforeachloop.IEnumerable<string> studentQuery7 = from student in students where student.Last == "Garcia" select student.First; Console.WriteLine("The Garcias in the class are:"); foreach (string s in studentQuery7) { Console.WriteLine(s); } // Output: // The Garcias in the class are: // Cesar // Debra // HugoCode earlier in this walkthrough indicated that the average class score is approximately 334. To produce a sequence of
Studentswhose total score is greater than the class average, together with theirStudent ID, you can use an anonymous type in theselectstatement:var studentQuery8 = 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 studentQuery8) { 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
Next Steps
After you are familiar with the basic aspects of working with queries in C#, you are ready to read the documentation and samples for the specific type of LINQ provider you are interested in:
See Also
Language-Integrated Query (LINQ) (C#)
Getting Started with LINQ in C#
LINQ Query Expressions



