EF Core
EF Core is an object-relational mapper: you write LINQ over objects, it emits SQL and materializes the rows back into entities. The deceptive part is that a call looks like working with an in-memory list while it costs a database round trip. Practical EF Core work comes down to two mechanisms, and interviews probe both. The first is the change tracker: a tracked query puts every entity into the ChangeTracker together with a snapshot of its original values, and SaveChanges diffs that snapshot against the current state to build the UPDATEs. The second is expression-tree translation: an IQueryable is not executed but accumulated, and only reaches the database at materialization.
Almost every trap grows from those two. Tracking costs memory and time — which is why read-only reads use AsNoTracking, and why mutating an entity read without tracking silently fails to save. Lazy loading turns iterating a collection into a loop of queries — that is N+1. A DbContext is a Unit of Work with mutable state, which is why it is registered Scoped, not Singleton. A migration is a diff of the model against a snapshot, not a command to the database. The layer-by-layer breakdown is below.
Topic map
- DbContext and DbSet — the database session, the unit of work, and
DbSet<T>as the queryable root and an already-finished repository. - Registration and lifetime —
AddDbContextgives aScopedcontext, one per HTTP request, and whySingletonbreaks here. - The change tracker and AsNoTracking — the original-values snapshot, identity resolution, the diff on
SaveChanges, and exactly whatAsNoTrackingskips. - Loading strategies — eager via
Include, explicit viaLoad(), lazy via proxies, and the price of each. - The N+1 problem — how touching a navigation inside a loop turns one query into fifty-one, and how a projection fixes it.
- Migrations — a diff of the model against the snapshot,
Up/Down,__EFMigrationsHistory, and the expand/contract scheme for zero-downtime deploys.
Common Mistakes and Traps
| Mistake | Consequence |
|---|---|
Sharing one DbContext across parallel operations | The context is not thread-safe — a second operation on the same instance throws InvalidOperationException |
Registering DbContext as a Singleton | One change tracker for the whole app — entities pile up and are never released, and requests see each other's state |
Mutating an entity read with AsNoTracking and calling SaveChanges | Zero commands: the entity has no EntityEntry, so there is nothing to diff — the edit is lost silently |
| Treating tracking as a row cache | A repeat query still hits the database; identity resolution merely returns the already-tracked instance instead of a new one |
| Leaving lazy loading on in an API | Every navigation read inside a loop is another query — that is N+1 |
Reaching for Include where a Select would do | The whole graph with all its columns is pulled into memory instead of the three fields you need |
Putting several collection Includes into one query | The JOIN multiplies rows into a cartesian product — you need AsSplitQuery() |
Expecting an untranslatable Where to be evaluated on the client | Since EF Core 3.0 that is an InvalidOperationException, not a silent degradation |
Hand-editing ModelSnapshot | The snapshot is the diff baseline — the next migration will be generated wrong |
| Renaming a column in a single migration during a rolling deploy | The old build talks to the same database and breaks — you need an expand/contract scheme |
Interview relevance
EF Core is a favourite interview topic precisely because it separates people who "can write Include" from people who understand what happens between LINQ and the row. The check is the execution model, not the API: how many round trips this code produces and what exactly lands in memory.
Typical checks:
- What a
DbContextis, why it is aUnit of Work, and why it isScopedrather thanSingleton. - The mechanics of the change tracker — snapshot, identity resolution, diff on
SaveChanges— and whatAsNoTrackingremoves. - The difference between eager / explicit / lazy loading and which of them produces N+1.
- How to find and fix N+1 (
Includeversus aSelectprojection). - What
dotnet ef migrations adddoes — and that it never touches the database. - How to ship a schema change to a live service running several instances.
Common wrong answer: "AsNoTracking is just an optimization, you can put it everywhere." That opens the discussion of what AsNoTracking actually removes — the original-values snapshot and identity resolution — and that without a snapshot SaveChanges has physically nothing to diff: the entity will not be saved, and no exception will be raised about it.