The method mapping and laziness
LINQ is the same shape as JS array methods with different names. Once you learn the dictionary you can read most of it on sight.
The trap is laziness. A JS .map().filter() runs immediately and gives you an array. A LINQ chain over IEnumerable<T> is a query description: nothing runs until you materialize it with ToList(), FirstOrDefault(), Any(), or a foreach. That deferral is what lets LINQ over a database translate the whole chain into SQL instead of running it in C#.
That’s also where the most common performance slop comes from. If you materialize too early, you pull the whole table into memory and filter in C#. Fusion anti-pattern #7: push the filter into the query. Where before ToListAsync, so the database does the work with its indexes.
// Wrong: loads every row, then filters in memory (O(N) table scan).
var all = await _db.FlightDisruptionCostCalcs.ToListAsync();
var mine = all.Where(x => x.FlightNumber == flightNumber);
// Right: the WHERE becomes SQL; the DB returns only matching rows.
var mine = await _db.FlightDisruptionCostCalcs
.Where(x => x.FlightNumber == flightNumber)
.ToListAsync();Try it yourself
Read a real query-store LINQ chain
Confirm the filter-then-materialize order in shipping code.
- Open
persistence/DisruptionDecisions/DecisionQueryStore.cs - Find
GetDecidedFlightIdentitiesand read the chain:.AsNoTracking().Where(...).Select(...).ToListAsync() - Note the
WhereandSelectcome BEFOREToListAsync, so they translate to SQL - Note
.AsNoTracking()at the front (Module 9 explains why every read query has it)
When does the query run
You write _db.Flights.Where(x => x.Active) and assign it to a variable. When does the database get hit?
Show answer
Correct answer: C — When you materialize it: ToListAsync, FirstOrDefaultAsync, Any, or foreach
LINQ over a DbSet is deferred. The chain is a query description; it executes as SQL only when you materialize it with ToListAsync/FirstOrDefaultAsync/Any/foreach.