Walkthrough: Writing Queries in C# (LINQ)
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
using
directive 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
Student
class 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
Student
class and the initialized list of students to theProgram
class 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
Student
to theStudents
list 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
Main
method, 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 wholeStudent
object 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 eachStudent
in 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
foreach
loop 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
foreach
loop.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
where
clause 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. Thewhere
clause should resemble the following code.where student.Scores[0] > 90 && student.Scores[3] < 80
For 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
orderby
clause orders the results in alphabetical order from A to Z according to the last name of each student. Add the followingorderby
clause to your query, right after thewhere
statement and before theselect
statement:orderby student.Last ascending
Now change the
orderby
clause 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] descending
Change the
WriteLine
format 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
Key
and 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
char
type as a key, and a sequence ofStudent
objects. Because the type of the query has changed, the following code changes theforeach
execution 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, Eugene
Run the application and view the results in the Console window.
For more information, see group clause.
To make the variables implicitly typed
Explicitly coding
IEnumerables
ofIGroupings
can quickly become tedious. You can write the same query andforeach
loop much more conveniently by usingvar
. Thevar
keyword does not change the types of your objects; it just instructs the compiler to infer the types. Change the type ofstudentQuery
and the iteration variablegroup
tovar
and rerun the query. Note that in the innerforeach
loop, the iteration variable is still typed asStudent
, and the query works just as before. Change thestudent
iteration variable tovar
and 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, Eugene
For 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
orderby
clause after thegroup
clause. But to use anorderby
clause, you first need an identifier that serves as a reference to the groups created by thegroup
clause. You provide the identifier by using theinto
keyword, 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, Eugene
When 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
let
keyword 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 Michael
For 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
Student
in the source sequence, and then calls theAverage()
method on the results of that query to calculate the average score of the class.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 theforeach
loop.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 // Hugo
Code earlier in this walkthrough indicated that the average class score is approximately 334. To produce a sequence of
Students
whose total score is greater than the class average, together with theirStudent ID
, you can use an anonymous type in theselect
statement: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
Feedback
Submit and view feedback for