← Home
.NETData accessReported by candidates

What is LINQ, and what is deferred execution? How do you page results with it?

Asked at Capgemini, Hexaware, ESQ, Krishiv and MaiVin, and reported again from TCS and Accenture rounds in September 2026.

Say this first · about 30 seconds
Make it yours · BMW FS payoff screen (invented example)
The code, simplified

Invented to show the shape, in C#. Tap Next to walk contract C-1042.

BMW.sln · where this file sits
  • Payoff/
Diagram · step 1Builds the query: payments for C-1042. Nothing runs yet.
LastPaymentQuery.cs · line 1 · 1 of 4The controller calls this for contract C-1042.

Fix later in your repo Needs a database index on (ContractId, PaidOn), a sorted lookup, if the payments table grows large.

Say it out loud, then check

Show the 30-second answer
  • LINQ is a set of query methods in C#, like Where, Select, OrderBy and GroupBy. They work the same way on a list in memory or on a database table through EF Core, the library that turns them into SQL.
  • Deferred execution means building a query doesn't run it. It runs when you enumerate it: a foreach, ToList, Count or First.
  • With EF Core that's a feature: I chain Where, OrderBy, Skip and Take, and EF sends one SQL query with the filter and the paging inside it.
  • The traps: switching to in-memory LINQ too early pulls the whole table, and enumerating the same query twice runs it twice.
Check your answer: tick what you said

You said 0 of 7

Then add your example: BMW FS payoff screen (invented example)
  • Situation: at BMW FS the screen showed a contract's last payment. The code used on the payments, with no order, so it returned whichever row SQL happened to give back.
  • What I did: I made the order explicit: by paid date, then , because a brand-new contract has no payments yet.
  • Result: the screen always shows the latest payment, and a new contract shows "no payments" instead of an error.
  • What I'd say: without on a database is a hidden bug: SQL doesn't promise any order unless you ask.

See it

1/6Before: the screen called first. All 20,000 rows came back, then C# filtered them.

In plain words

The senior answer · about 2 minutes
What LINQ is
  • LINQ, Language Integrated Query, is a set of methods like , , , and that you chain to describe what you want. Each takes a lambda, a small inline method; the explains those. The same code reads a list in memory or, through EF Core, a SQL table.
  • The difference is the type you start from. A is an : LINQ runs in .NET, item by item. EF Core's is an : LINQ doesn't run at all, it builds an expression tree, a description of the query kept as data, which EF reads and translates into one SQL statement.
Deferred execution
  • A LINQ query is a recipe, not the meal. and return a query object immediately and touch no data. The work happens when something enumerates it: a , , , , .
  • That's what makes paging cheap with EF Core: I add , , and , and only sends one SQL query with the filter, order and offset in it. Twenty rows come back, not twenty thousand.
  • It's also the source of two classic bugs. Enumerate the same query twice and it runs twice, two database round trips or two passes over a big list. And it reads the data as it is when it runs, not when you wrote it, so a list changed in between gives a different answer.
Where it goes wrong in production
  • The expensive one: a method EF can't translate, so someone adds or before the to make the error go away. It works, and it now loads the whole table into memory to filter it. That's the incident on this card.
  • I check the SQL a query really sends, with ToQueryString() or EF's logging in development, and I keep translatable filters first, then switch to memory only for the small remainder.
  • vs is about intent: says "there must be exactly one" and throws if the data breaks that rule, so it catches duplicates. It asks the database for two rows to check. On a unique key I use ; for "the latest payment" I use with .

All in one flow

Coming Not written yet: the card pass adds it.

The story

  1. Before

    UDC Finance's collections team works through overdue contracts every morning on a screen that shows 20 at a time. The first version loaded all contracts with , then picked the overdue ones and the page in C#.

  2. Why it had to change

    That was fine with 2,000 contracts in test. In production there were 20,000, so every page click pulled 20,000 rows across the network to show 20. The screen took seconds, and memory jumped on each click.

  3. How it works now

    LINQ queries are deferred: , , and only describe the query. Built on EF Core's , the whole description becomes one SQL statement when runs it. So the fix was to move to the end: filter, order and page in the database, bring back 20 rows. The diagram walks it.

  4. What happens next

    One query per page, 20 rows each. The same deferred behaviour has one trap left: enumerate a query twice and it runs twice, so the result is turned into a list once and reused.

  5. What we gained

    The database does the filtering it's built for, the API moves 20 rows instead of 20,000, and the code reads like the question the user asked.

In the lending API

The whole query is built first and runs once, at , as one SQL statement.

Only the key lines are shown; Deep walks the whole file.

Lending.sln · where this file sits
  • src/Application/Collections/
⋯ 1 line in Deep⋯ 4 lines in Deep⋯ 3 lines in Deep
OverdueQuery.cs · line 5 · 1 of 4_db is our EF Core database context; _db.Contracts is the contracts table. : a description EF Core will turn into SQL. No data yet.

The code, file by file

The UDC Finance collections screen in the lending API: overdue contracts, oldest first, a page at a time. Tap Next to walk the diagram with the code.

Lending.sln · where these 2 files sit
  • src/Application/
    • Collections/
    • Contracts/
Diagram · step 1Before: the screen called first. All 20,000 rows came back, then C# filtered them.
OverdueQuery.cs · line 1 · 1 of 11The collections controller calls it with the page number from the URL, for example page 2 of 20 rows.

What they ask next

The follow-ups interviews really ask. Answer out loud, then tap to check.

Follow-up tree

Answer, then open the next level. That is the order an interviewer digs in.

Reporteda candidate was asked Intervieweran interviewer asks it

Production bugs, and how to tell them

Each one: what you see, why, the fix in code, and the line to say.

Production incident: The arrears export loaded 1.8 million contracts to find 900

P2 · serious, has a workaroundUDC Financebad dataNightly arrears export (a .NET worker on Azure)

Composed, not a real event at UDC Finance. The mechanism is real EF Core: AsEnumerable before Where moves the filter into memory, and EF's own error message suggests it. Dates, counts, names, tickets and the query are composed.

The story in 5 lines
  1. On Tue 13 Oct the nightly arrears export for UDC Finance ran out of memory and the restarted. The collections team had no call list at 07:00.
  2. The week before, a data migration had loaded 1.6 million closed historical contracts into the same table. The table went from 200,000 rows to 1.8 million.
  3. The export query had before its , added months earlier to get past EF Core's "could not be translated" error. So it loaded every contract into memory and filtered in C#.
  4. Nothing alerted on growth: with 200,000 rows the waste was invisible. With 1.8 million, the hit its memory limit.
  5. The fix put the filter back in SQL as an expression EF can translate. The export now reads about 900 rows, and a test checks the SQL has a WHERE.
Who's who (4 people)
You
.NET developer on the vendor team. Finds the cause, writes the fix and the test.
Tech lead
Vendor senior. Reviews the hotfix, runs the postmortem.
L2 pod
Vendor support. Sees the failed run, reruns the export.
UDC collections manager
Needs the arrears list at 07:00 NZ time for the morning calls.

Tap a step. The map lights the place where it happens.

1/4 Detect

Detect: the export died at 02:41 with out-of-memory. No call list at 07:00.

Tue 13 Oct, 02:41 NZDT (Mon 12 Oct, 19:11 IST)The on-call L2 pod, then you

  • What: the arrears export crashed with at 02:41 and restarted. The file the collections team loads at 07:00 was never written.
  • Why it hid: the same code had run every night for months. With 200,000 contracts it used a lot of memory, but under the limit, so nothing alerted.
  • How we saw it: showed the exception, and the 's memory chart climbed in a straight line from 02:00 until the crash.
App Insights: the crash and the memory climb
exceptions
| where timestamp > ago(12h)
| where type has "OutOfMemory"
| project timestamp, outerMessage
In theory

An export should alert when it reads far more rows than it writes, or when its run time or memory jumps.

In practice

The only alert was on the crashing, so months of reading the whole table looked normal.

Try it yourself · 10 minutes, on your Mac
  1. In a console app with EF Core and SQLite, add 1,000 contracts, 10 of them more than 30 days overdue.
  2. Query with .AsEnumerable().Where(c => c.DaysOverdue > 30) and print the SQL with EF logging: no WHERE.
  3. Move the before and print ToQueryString(): now it has WHERE.
  4. Add a C# method to the and run it without : read the "could not be translated" error.
You should see

Where AsEnumerable sits decides where the filter runs. Look at the SQL, not only at the result.

The senior move

"AsEnumerable before a Where looks harmless and returns the right answer, but it loads the whole table. I keep filters translatable so SQL does them, I check ToQueryString in review, and I alert when a job reads far more rows than it writes."

Sources Sourced: EF Core's client-evaluation rules and the exact "could not be translated ... AsEnumerable" message (Microsoft Learn, client vs server evaluation); AsEnumerable before Where loads all rows (a run on .NET 10 with EF Core 9 and SQLite showed SELECT without WHERE, 3 of 5 rows kept in memory). Expression<Func<T,bool>> is translatable; a compiled method call is not.

Traps

Traps

What should you say instead? Tap to check.

Live coding

Easy first, then harder. Set a timer and type it before you look.

Task 1

Reportedeasy · 15 minutes

Write a LINQ query that lists every contract with its total paid and number of payments, including contracts with no payments (a left join: keep every contract, even with no match), highest total first.

BayGrape: LINQ left join between two tables
Stuck? Hints
  1. Which join keeps contracts that have no payments?
  2. gives each contract the list of its payments, empty if none.
  3. What does return on an empty list?
Show a solution
Lending.sln · where this file sits
Program.cs · line 2 · 1 of 3A left join: every contract, each with the list of its payments (empty if none).
What the interviewer checks
  • Contracts with no payments appear, with 0.
  • (or query syntax with into and ), and can say which.
  • on the total.
  • Projects to a small object, not whole entities.
  • Senior extra: with EF Core, c.Payments.Sum(...) through the navigation property does the same in one query.
With AI tools

Copilot and EF Core's own error message both push the same wrong fix. When a query uses a C# method that EF can't turn into SQL, the error itself offers to "switch to client evaluation explicitly by inserting a call to 'AsEnumerable'" or , and assistants suggest exactly that. In review I check where any or sits: after the database filters is fine; before them it loads the whole table. I also check ToQueryString() output for anything that should have been a WHERE.

Glossary: every name, one line each →

My notes

Paste the NOTES block from your doubt chat here. The grader never reads notes; the coach reads "Still unclear".

Read moreWhere it was asked