EF Core or Dapper? How I decide
EF Core and Dapper aren’t enemies — they’re tools. I use both depending on the shape of the problem. The trick is knowing when the abstraction helps vs when it just gets in the way.
1. I default to EF Core for most CRUD
For typical domain models and business CRUD, EF Core wins:
- Change tracking and unit-of-work are baked in.
- Migrations and schema evolution are first-class.
- LINQ keeps a lot of code expressive and readable.
2. I reach for Dapper for hot paths & read-heavy endpoints
Dapper shines when:
- You need very predictable, hand-tuned SQL.
- You’re shaping data into read models or dashboards.
- You want minimal overhead and full control over joins.
3. It’s fine to mix them (with boundaries)
In bigger systems I’ll happily use both:
- EF Core for core transactional aggregates.
- Dapper for reporting, background projections, or “expensive” queries.
The only rule: don’t let them fight over the same unit of work. Keep the boundaries clear.
4. Watch your N+1s and lazy loading
With EF Core, I avoid lazy loading in APIs and background services. I’d rather:
- Use explicit
.Include/.ThenInclude. - Project directly into DTOs with
Select. - Log slow queries and fix them early.