This page is for LINQ interview questions that follow the "what does Where do" warm-up. The interviewer wants to know whether your query runs in SQL or in the process.
EF Core context: EF Core interview questions and SQL performance. Hub: interview questions.
Real-world analogy
LINQ is a waiter writing down an order. The pad is the query. The kitchen has not cooked anything until the waiter tears off the sheet and hands it in. That tear is ToList or First. Adding "no onions" after the kitchen has plated is a second pass on the plate, not a change to the ticket. In EF Core the kitchen is SQL Server. In a List the kitchen is the process you are already standing in.
Worked example
A reviewer sees orders.Where(o => o.IsOpen).Select(o => o.Total) and asks why the page is slow. The variable orders is already a List<Order> from an earlier ToList. The Where is C#, and the totals are summed after every open order was loaded, including the columns the page never shows. The fix is to filter and project before the materializing call, then Sum in SQL if the page only needs the number. Deferred execution is the reason this compiled and still did the wrong thing: nothing ran at the Where line.
Short answers
| Question | Crisp answer |
|---|---|
| Deferred execution | The query runs when you iterate or call ToList, not when you write Where |
| LINQ to Objects | IEnumerable. Runs in memory |
| LINQ to Entities | IQueryable. Builds SQL until you materialize |
| Multiple enumeration | Two loops, two executions |
| Client evaluation | A predicate EF cannot translate, so it pulls rows into the app |
The questions they ask
- Why
customers.Where(...)does not hit SQL yet - Why the same query runs twice
- Why
DateTime.Nowinside a query is a bug - Why a local
IsVipmethod blows up or scans the table SelectbeforeWhereand what that does to SQLFirstversusFirstOrDefaultversusSingle
Scenario 1: deferred execution
Prompt: This code logs one SQL. Then the method counts and then loops. Production shows two queries. Why?
IQueryable<Order> open = db.Orders.Where(o => o.Status == OrderStatus.Open);
var count = await open.CountAsync(ct);
var page = await open.OrderBy(o => o.Id).Take(20).ToListAsync(ct);
Answer: CountAsync and ToListAsync each execute. That is correct if you need both a count and a page, and you should say so. The bug is assuming Where already loaded rows. If you only need the page, do not count. If you need both, two round-trips are honest. Do not .ToList() the whole table to count in memory.
Scenario 2: client evaluation
Prompt: Where(o => IsOverdue(o)) worked in a unit test with a List<Order> and timed out in production.
Answer: The test was LINQ to Objects. EF Core needs a translatable expression. IsOverdue is an opaque method. Write the comparison inline (o.DueOn < DateTime.UtcNow) or use a mapped computed column. Do not "fix" it by ToList() then filtering. That downloads the table.
Scenario 3: First, FirstOrDefault, Single
Prompt: SingleAsync on a lookup by email starts throwing in a migration.
Answer: Single means zero or one, and it throws if there are two. After a bad import there are two rows. First hides the data bug. Say which one you want: Single when the invariant is one row, FirstOrDefault when missing is normal. Do not use Single as a synonym for First.
Related: IEnumerable vs IQueryable.