Source
A collection such as customers, products or orders.
C# guide
Learn LINQ with short C# examples, exact output, and side-by-side explanations of query syntax, method syntax, deferred execution, filtering, projection, grouping, joins and aggregation.
LINQ, or Language Integrated Query, is a C# feature that lets you query collections using readable, composable expressions. You can use it to filter lists, transform objects, sort results, group records, join collections and calculate totals.
This page focuses on LINQ to Objects, which means the examples run against in-memory C# collections such as List<T> and arrays.
A collection such as customers, products or orders.
A chain of operations such as Where, Select and OrderBy.
The moment the query is actually evaluated, often during enumeration.
Many LINQ operators use deferred execution. That means the query is created first, but the actual work happens later, usually when you loop through it or materialise it with ToList(), ToArray() or similar methods.
var products = new List<string> { "Keyboard", "Mouse" };
var query = products.Where(p => p.StartsWith("M"));
products.Add("Monitor");
var result = query.ToList();
All examples below reuse the same small dataset. This makes it easier to compare operators without learning a new business scenario each time.
public record Customer(int Id, string Name, string City, bool IsVip);
public record Supplier(int Id, string Name, string Country);
public record Product(int Id, string Name, string Category, decimal Price, int SupplierId);
public record Order(int Id, int CustomerId, decimal Total);
var customers = new List<Customer>
{
new(1, "Ada", "London", true),
new(2, "Ben", "Bristol", false),
new(3, "Cara", "London", true),
new(4, "Dan", "Leeds", false)
};
var suppliers = new List<Supplier>
{
new(1, "Northwind Input", "UK"),
new(2, "Pixel Displays", "DE"),
new(3, "Office Glow", "UK")
};
var products = new List<Product>
{
new(1, "Keyboard", "Hardware", 50m, 1),
new(2, "Mouse", "Hardware", 25m, 1),
new(3, "Monitor", "Display", 200m, 2),
new(4, "Desk Lamp", "Office", 30m, 3),
new(5, "Laptop Stand", "Office", 45m, 3)
};
var orders = new List<Order>
{
new(1001, 1, 75m),
new(1002, 2, 200m),
new(1003, 1, 75m)
};
LINQ can be written in query syntax or method syntax. Query syntax looks closer to SQL, while method syntax is fluent and works with every standard LINQ operator.
var names =
from c in customers
where c.City == "London"
select c.Name;
var names = customers
.Where(c => c.City == "London")
.Select(c => c.Name);
The examples below cover the most useful LINQ operators for day-to-day C# development.
| Category | Operators | Use case |
|---|---|---|
| Filtering | Where |
Keep only items matching a condition. |
| Projection | Select, SelectMany |
Transform objects into a new shape. |
| Ordering | OrderBy, ThenBy |
Sort results by one or more fields. |
| Grouping | GroupBy |
Group items by a shared key. |
| Joins | Join, GroupJoin |
Combine related collections. |
| Aggregation | Count, Sum, Average |
Calculate totals, counts and averages. |
var affordableOfficeProducts = products
.Where(p => p.Category == "Office" && p.Price < 50m)
.Select(p => p.Name)
.ToList();
var productLabels = products
.Select(p => $"{p.Name}: £{p.Price}")
.ToList();
var orderedProducts = products
.OrderBy(p => p.Category)
.ThenByDescending(p => p.Price)
.Select(p => $"{p.Category}: {p.Name}")
.ToList();
var categoryCounts = products
.GroupBy(p => p.Category)
.Select(g => $"{g.Key}: {g.Count()}")
.OrderBy(x => x)
.ToList();
var supplierProducts = products
.Join(
suppliers,
product => product.SupplierId,
supplier => supplier.Id,
(product, supplier) => $"{product.Name}: {supplier.Name}"
)
.ToList();
var orderSummary = new
{
Count = orders.Count(),
Revenue = orders.Sum(o => o.Total),
Average = Math.Round(orders.Average(o => o.Total), 2)
};
var checks = new
{
HasVip = customers.Any(c => c.IsVip),
AllHaveCity = customers.All(c => !string.IsNullOrWhiteSpace(c.City)),
HasLargeOrder = orders.Any(o => o.Total > 150m)
};
var pageSize = 2;
var pageTwo = products
.OrderBy(p => p.Id)
.Skip(pageSize)
.Take(pageSize)
.Select(p => p.Name)
.ToList();
LINQ is expressive, but a few details matter. Deferred execution can rerun work. Sorting and grouping may buffer the source. Materialising with ToList() gives you a snapshot, but it also uses memory immediately.
| Pitfall | Why it matters | Safer approach |
|---|---|---|
Using Single too casually |
It throws if there are zero or multiple matches. | Use FirstOrDefault when duplicates are not a bug. |
Calling OrderBy twice |
The second OrderBy replaces the first ordering. |
Use ThenBy for secondary sorting. |
Adding ToList() everywhere |
It forces immediate execution and allocates memory. | Materialise only when you need a snapshot. |
Assuming Distinct sorts data |
It removes duplicates, but it is not a presentation sort. | Add OrderBy when order matters. |
A good LINQ sample should be easy to test. Keep queries pure, return concrete results and assert the output.
Assert.Equal(
new[] { "Ada", "Cara" },
customers
.Where(c => c.City == "London")
.Select(c => c.Name)
.ToList()
);
Assert.Equal(350m, orders.Sum(o => o.Total));
Assert.Throws<InvalidOperationException>(
() => customers.Single(c => c.City == "London")
);