LINQ

C# · Practical · 15 min

LINQ is the idiomatic way to work with data in C#, from in-memory lists to databases via EF Core.

LINQ (Language Integrated Query) is a set of extension methods on IEnumerable<T> in System.Linq. You chain them into a pipeline:

  • Where(pred) filters, Select(f) projects, SelectMany flattens.
  • OrderBy, ThenBy, OrderByDescending sort.
  • GroupBy(keySelector) produces groups with a Key.
  • First, FirstOrDefault, Single, Any, All, Count, Sum, Average, Min, Max, MaxBy.
  • Distinct, Take, Skip, Chunk, Zip, Join, ToDictionary, ToLookup.
  • .NET 9 added CountBy and AggregateBy.

Most operators are lazy (deferred execution): Where and Select build a query that runs only when you iterate it or call a materializing method like ToList(), ToArray(), Count() or First(). A query enumerated twice runs twice, so materialize once if you reuse the results or the source is expensive.

There is also query syntax (from p in people where p.Age > 30 select p.Name), which compiles to the same method calls; method syntax is more common.

The same operators on IQueryable<T> (Entity Framework Core) are translated into SQL instead of running in memory, which is why LINQ knowledge carries over to data access.

First throws if nothing matches; FirstOrDefault returns default (null or 0). Single additionally throws if more than one item matches.

Example

var people = new List<Person>
{
    new("Ada", "London", 36), new("Alan", "London", 41),
    new("Grace", "NYC", 85), new("Linus", "Helsinki", 28),
};

var names = people.Where(p => p.Age > 30)
                  .OrderBy(p => p.Name)
                  .Select(p => p.Name);
Console.WriteLine(string.Join(", ", names)); // Ada, Alan, Grace

foreach (var g in people.GroupBy(p => p.City))
    Console.WriteLine($"{g.Key}: {g.Count()}"); // London: 2 / NYC: 1 / Helsinki: 1

Console.WriteLine(people.Average(p => p.Age)); // 47.5
Console.WriteLine(people.MaxBy(p => p.Age)!.Name); // Grace
Console.WriteLine(people.Any(p => p.City == "Paris")); // False
var byCity = people.CountBy(p => p.City).ToDictionary();
Console.WriteLine(byCity["London"]);        // 2

record Person(string Name, string City, int Age);

Remember

  • Chain Where, Select, OrderBy, GroupBy into readable pipelines
  • Most LINQ operators are lazy until enumerated or materialized
  • Call ToList() once when you reuse results
  • The same operators translate to SQL on IQueryable

Watch out for

  • Enumerating a deferred query several times, re-running expensive work each time.

Check yourself

  1. var q = list.Where(x => x > 2); list.Add(10); How many items does q.Count() see for list = [1, 3]?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: learn.microsoft.com/en-us/dotnet/csharp/linq/