Performance Optimization
Level: Intermediate
ℹ️ Where This Fits
Unoptimized queries scale poorly. Understanding lazy loading, change tracking, and query translation prevents performance bottlenecks as data grows.
ℹ️ What You'll Learn
- AsNoTracking: Disable change tracking for read-only queries (performance boost)
- Lazy loading: Access related data causes extra queries (danger in loops)
- Eager loading: Include related data upfront (solves N+1)
- Query translation: LINQ translated to SQL (check generated SQL)
- ExecuteUpdate/ExecuteDelete: Bulk operations without loading entities (EF Core 7+)
- Indexes: Create database indexes on frequently filtered columns
- Select projection: Load only needed columns (not entire entity)
- Split queries: Execute separate queries for better performance (SplitQuery)
- Query caching: Entity Framework caches compiled queries
- ChangeTracker.Clear: Clear tracking for memory efficiency (large datasets)
- Profiling: Enable SQL logging to see generated queries
- Common mistakes: Tracking read-only data, lazy loading in loops, complex includes
AsNoTracking for Read-Only
// Bad - tracks 10,000 entities (memory intensive)
var students = await _context.Students
.ToListAsync();
// Good - no tracking (read-only API response)
var students = await _context.Students
.AsNoTracking()
.ToListAsync();
Bulk Update - ExecuteUpdate (EF Core 7+)
// Bad - loads all, modifies, saves (slow for large datasets)
var studentsToUpdate = await _context.Students
.Where(s => s.Status == StudentStatus.Inactive)
.ToListAsync();
foreach (var student in studentsToUpdate)
{
student.Status = StudentStatus.Graduated;
}
await _context.SaveChangesAsync();
// Good - single SQL UPDATE statement
var rowsAffected = await _context.Students
.Where(s => s.Status == StudentStatus.Inactive)
.ExecuteUpdateAsync(s => s
.SetProperty(x => x.Status, StudentStatus.Graduated));
Split Queries - Multiple Queries Better Than Complex Joins
// Single large query (can be slow with large datasets)
var students = await _context.Students
.Include(s => s.Exams)
.Include(s => s.FeeAccounts)
.ToListAsync();
// Split into multiple queries (often faster)
var students = await _context.Students
.Include(s => s.Exams)
.Include(s => s.FeeAccounts)
.AsSplitQuery()
.ToListAsync();
Projection - Load Only Needed Columns
// Bad - loads entire Student object (all columns)
var students = await _context.Students
.Where(s => s.ClassName == "10-A")
.ToListAsync(); // Gets Id, Name, ClassName, DateOfBirth, Status, etc
// Good - loads only Name and Roll Number
var studentNames = await _context.Students
.Where(s => s.ClassName == "10-A")
.Select(s => new
{
s.Name,
s.RollNumber
})
.ToListAsync();
Disable Change Tracking for Large Imports
// Importing 100,000 students
using (var context = new SmsDbContext())
{
context.ChangeTracker.AutoDetectChangesEnabled = false;
var students = GetStudentsToImport(); // 100,000
foreach (var batch in students.Chunk(1000))
{
_context.Students.AddRange(batch);
await _context.SaveChangesAsync();
_context.ChangeTracker.Clear(); // Clear to free memory
}
}
Enable SQL Logging to Profile
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
options
.UseSqlServer(connString)
.LogTo(
logger: Console.WriteLine,
filter: (category, level) =>
category == DbLoggerCategory.Database.Command.Name &&
level == LogLevel.Information,
logBehavior: DbContextLogBehavior.Instant
)
.EnableSensitiveDataLogging(); // Log parameter values
}
Best Practices
✓ Use AsNoTracking for read-only queries ✓ Include related data to prevent N+1 ✓ Project only needed columns ✓ Use ExecuteUpdate for bulk changes ✓ Profile queries with SQL logging
💡 When to Optimize
- Write simple, correct code first
- Profile and identify bottleneck
- Optimize based on data: AsNoTracking, Projection, Indexes
- Don't premature optimize
🤖Use AI to Learn Faster
Use ChatGPT, Claude, or Copilot to go deeper on Performance Optimization. Try these prompts:
"When should you use AsNoTracking?""What's the difference between Include and SplitQuery?""How do you do bulk updates without loading entities?""When is Select projection important for performance?"
💡 Tip: After reading this article, paste your own code into AI and ask "What could go wrong here and why?" — fastest way to find edge cases and deepen understanding.
Next Article
nexcoding.in
Have questions on your tech stack, ongoing projects, or need one-to-one training?