What is LINQ?
LINQ is ETL for the data in your program. Start from any collection, chain methods such as Where, OrderBy and Select that each transform the items, and finish with ToList, Count or First. A query runs when its results are used.
Journeyman · C# · Updated
After this lesson you can
- Chain LINQ methods from a source, through transformations, to a result such as ToList or Count
- Explain that a LINQ query runs when its results are used, not when it's written, and use ToList to keep the results
Key takeaways
- A LINQ chain starts from any collection, transforms it with methods such as
Where,OrderByandSelect, and ends with a result such asToList()orCount(). - LINQ never changes the original collection; it always builds something new from it.
- A LINQ query runs when its results are used, such as by a
foreachorCount(), not when it's written. This is deferred execution. - End a chain with
ToList()to keep the results as they are now, so they won't change behind your back or run the query again.
LINQ (Language-Integrated Query, said "link") is a set of methods for working with collections: filtering, sorting, reshaping, totaling. It's ETL for the data inside your program:
| ETL | In LINQ | Example |
|---|---|---|
| Extract from a source | Any collection: a list, an array, a set, the lines of a file | names |
| Transform, one step at a time | Methods that each pass on a changed stream of items | .Where(...), .OrderBy(...), .Select(...) |
| Load into a destination | A method that turns the stream back into data at rest | .ToList(), .Count(), .First() |
From a loop to LINQ
Here's a job you can already do with a loop: the names longer than 4 letters, in alphabetical order, in capitals.
List<string> names = new List<string> { "Olivia", "Ethan", "Sophia", "Liam", "Emma", "Noah", "Ava", "Jackson" };
List<string> result = new List<string>();
foreach (string name in names)
{
if (name.Length > 4)
{
result.Add(name.ToUpper());
}
}
result.Sort();
Console.WriteLine(string.Join(", ", result));
ETHAN, JACKSON, OLIVIA, SOPHIA
And here's the same job with LINQ:
List<string> names = new List<string> { "Olivia", "Ethan", "Sophia", "Liam", "Emma", "Noah", "Ava", "Jackson" };
List<string> result = names
.Where(name => name.Length > 4)
.OrderBy(name => name)
.Select(name => name.ToUpper())
.ToList();
Console.WriteLine(string.Join(", ", result));
ETHAN, JACKSON, OLIVIA, SOPHIA
Read it top to bottom, like a recipe: take the names, keep the ones longer than 4, put them in order, make each one capitals, and collect them into a list. Each line is one step, and each step gets a lambda saying what to do with one item.
The loop says how to do it: make a list, loop, check, add, sort. LINQ says what you want, and reads almost like the sentence describing it. (It's the same thinking as SQL: WHERE, ORDER BY and SELECT are there for a reason.)
The steps
- The source can be any collection, because LINQ's methods work on
IEnumerable<T>: the "you can go through my items one by one" contract from Software Interfaces. Lists, arrays, sets, dictionaries andFile.ReadLinesall keep it. LINQ's methods come withusing System.Linq;, which every project already has built in. - The transformations each take a stream of items and hand on a new one.
Wherehands on fewer items,OrderBythe same items in a new order,Selecta changed item for each one. - The destination ends the chain:
ToList()collects the items into aList<T>,ToArray()into an array, and methods such asCount(),Sum()andFirst()reduce them to one value.
The original list is never changed. LINQ always builds something new from it.
A query runs when it's used
Here's something surprising. Without the last step, a LINQ chain doesn't do anything yet. It's a recipe, not a meal: it runs when something asks for its items, such as a foreach, ToList() or Count().
List<int> scores = new List<int> { 72, 95, 48 };
IEnumerable<int> passes = scores.Where(score => score >= 60);
scores.Add(88);
Console.WriteLine(passes.Count());
3
passes was written when the list had two passing scores, but it counts 3, because the query only ran at passes.Count(), after 88 was added. This is called deferred execution: the work is put off until the results are needed.
When you want the results now, frozen as they are, finish with ToList():
List<int> scores = new List<int> { 72, 95, 48 };
List<int> passes = scores.Where(score => score >= 60).ToList();
scores.Add(88);
Console.WriteLine(passes.Count);
2
A good habit: end a chain with ToList() (or Count(), First(), …) unless you have a reason not to. Then the result is data at rest, and it won't change behind your back or run the query again every time you loop over it.
Check yourself
2 questions to try on the lesson page.
So what
LINQ turns "make a list, loop, check, add" into one readable sentence: source, transformations, destination. It's how most C# code works with collections, and the same methods will query a database through Entity Framework Core later in this module.