EF Core LINQ Queries

1 line of C# = 1000 SQL queries. Learn to not get fired. 5 mins.

Your First "Aha" Moment: EF Core doesn't run your LINQ. It translates it to SQL. Write bad LINQ = SQL from hell.

1. The N+1 Problem That Kills Prod

JUNIOR CODE:

var users = await _db.Users.ToListAsync();
foreach(var u in users) {
    Console.WriteLine(u.Orders.Count); // 1 SQL per user!
}

What happens: 1 query for users + 1000 queries for orders = 1001 roundtrips. DB CPU 100%. App timeout. You get PagerDuty at 3 AM.

STAFF FIX:

var users = await _db.Users.Include(u => u.Orders).ToListAsync(); // 1 SQL with JOIN

Rule: If you loop and access navigation property, you need Include.

2. AsNoTracking - The 90% Perf Win

// SLOW: Tracks 10k objects. 100MB RAM wasted for read-only page.
var products = await _db.Products.ToListAsync();

// FAST: No tracking. 5x less memory. 2x faster.
var products = await _db.Products.AsNoTracking().ToListAsync();

Rule: If you don't call SaveChanges() after, use AsNoTracking(). Reports, lists, GET APIs = always.

3. First vs Single vs FirstOrDefault

MethodIf 0 rowsIf 2+ rowsUse when
FirstAsync()ThrowsTakes firstYou're 100% sure it exists
FirstOrDefaultAsync()Returns nullTakes firstDefault for APIs
SingleAsync()ThrowsThrowsDB constraint guarantees 1 row
Career-Killer Mistake #1: _db.Users.ToList() then .Where() in C#
What happens: You download 1M users to RAM, then filter. DB query = SELECT * FROM Users. 10GB RAM, 30s query.
Fix: Always filter in DB: _db.Users.Where(u => u.IsActive).ToListAsync(). Check SQL via .ToQueryString().
Stop Here. Think. IQueryable = SQL not run yet. ToListAsync() = runs SQL.
Next: Relationships. This is where Include hell begins.

Quick Check ๐Ÿง 

Ready for Foreign Keys? You can query fast. Next: Relationships teaches HasOne, WithMany, cascade delete, and why DELETE FROM Users fails. Let's go โ†’

Comments on LINQ Queries (0)

No comments yet. Be the first to share your thoughts!