Skip to main content

Entity Framework Core Fundamentals

Level: Intermediate

ℹ️ Where This Fits

EF Core is an ORM (Object-Relational Mapper). Learn this after understanding databases and basic SQL. EF Core generates SQL automatically, reducing code but hiding details. Great for rapid development but requires understanding lazy loading, N+1 queries, and change tracking.

ℹ️ What You'll Learn
  • Entity Framework Core: ORM that maps database tables to C# objects (higher abstraction than ADO.NET)
  • DbContext: Bridge between code and database (Contains DbSet properties for each entity)
  • Entity models: C# classes that map to database tables (properties = columns, relationships = foreign keys)
  • Code-First approach: Define C# models first, EF generates database schema from models (via migrations)
  • Database-First approach: Create database first, EF scaffolds models from existing schema
  • Migrations: dotnet ef migrations add CreateStudentTable generates SQL, dotnet ef database update applies it
  • Migration workflow: Model change → migrations add → review generated SQL → database update → database changes
  • LINQ to Entities: _context.Students.Where(s => s.ClassName == "10A") translates to SQL automatically
  • .Include() eager loading: _context.Students.Include(s => s.Exams) prevents N+1 queries (load related data upfront)
  • .ThenInclude(): Nested includes (Include(s => s.Exams).ThenInclude(e => e.Subjects)) load multiple relationship levels
  • One-to-Many: Student has many Exams (foreign key ExamId in Exam table points to Student)
  • Many-to-Many: Student and Subject through StudentSubject junction table with composite key
  • Navigation properties: student.Exams accesses related objects (lazy or eager loaded depending on configuration)
  • Change tracking: EF automatically tracks entity modifications (student.Name = "New"; SaveChanges() generates UPDATE)
  • SaveChanges(): Commits all pending changes to database (INSERT, UPDATE, DELETE in single transaction)
  • SaveChangesAsync(): Async version of SaveChanges (preferred in modern applications)
  • Lazy loading: Access related data only when accessed (danger: N+1 queries, performance issue)
  • N+1 query problem: 1 main query + N queries for related data (fix: use .Include() to eager load)
  • Query filtering: .Where(), .Select(), .OrderBy() filter/project results (executes on database, not in memory)
  • .AsNoTracking(): Query without change tracking (read-only scenarios, better performance for large queries)
  • Batch operations: Update multiple rows with .ExecuteUpdate(), delete with .ExecuteDelete() (EF Core 7+)
  • Transactions: using (var trans = _context.Database.BeginTransaction()) for multi-operation consistency
  • Connection string configuration: In OnConfiguring or dependency injection (.UseSqlServer in Startup)
  • School Management entities: Student, Teacher, Subject, Exam, ExamResult, Fee, FeePayment, Attendance, AuditLog
  • Common mistakes: Missing .Include() (N+1 queries), lazy loading in loops, forgetting SaveChanges(), tracking deleted entities
  • When to use EF Core: Rapid development, complex relationships, database migrations, small-medium datasets

What is EF Core?

ERM = Entity Framework Core

Object-Relational Mapper. Maps database tables to C# classes. High-level abstraction over ADO.NET.

ADO.NET ← Raw SQL, parameters, SqlCommand
EF Core ← LINQ, automatic SQL generation
Dapper ← SQL-first, light mapping

DbContext and Entities

DbContext = bridge between code and database.

using Microsoft.EntityFrameworkCore;

public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public string RollNumber { get; set; }
public string ClassName { get; set; }
public StudentStatus Status { get; set; }
}

public enum StudentStatus { Active, Inactive, Graduated }

public class SmsDbContext : DbContext
{
public DbSet<Student> Students { get; set; }
public DbSet<Exam> Exams { get; set; }
public DbSet<ExamResult> ExamResults { get; set; }

protected override void OnConfiguring(DbContextOptionsBuilder options)
{
options.UseSqlServer("Server=localhost;Database=SmsDb;Trusted_Connection=true;");
}
}

Code-First Approach

Define classes first. EF creates database schema.

public class Student
{
[Key]
public int Id { get; set; }

[Required]
[StringLength(100)]
public string Name { get; set; }

[Required]
[StringLength(50)]
public string RollNumber { get; set; }

[Required]
[Range(1, 12)]
public int ClassNumber { get; set; }

[Required]
public StudentStatus Status { get; set; }

public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}

Migrations

Generate SQL from classes.

dotnet ef migrations add InitialCreate
dotnet ef database update

Creates: Students table with columns from properties.

Basic LINQ Queries

public class StudentService
{
private readonly SmsDbContext _context;

public StudentService(SmsDbContext context)
{
_context = context;
}

// Get all students
public async Task<List<Student>> GetStudentsAsync()
{
return await _context.Students.ToListAsync();
}

// Get by ID
public async Task<Student> GetStudentAsync(int id)
{
return await _context.Students.FirstOrDefaultAsync(s => s.Id == id);
}

// Filter
public async Task<List<Student>> GetStudentsByClassAsync(string className)
{
return await _context.Students
.Where(s => s.ClassName == className)
.ToListAsync();
}

// Count
public async Task<int> GetStudentCountAsync()
{
return await _context.Students.CountAsync();
}

// Order
public async Task<List<Student>> GetStudentsOrderedAsync()
{
return await _context.Students
.OrderBy(s => s.Name)
.ToListAsync();
}
}

Insert, Update, Delete

public async Task CreateStudentAsync(Student student)
{
_context.Students.Add(student);
await _context.SaveChangesAsync();
}

public async Task UpdateStudentAsync(int id, Student updatedStudent)
{
var student = await _context.Students.FindAsync(id);
if (student != null)
{
student.Name = updatedStudent.Name;
student.Status = updatedStudent.Status;
await _context.SaveChangesAsync();
}
}

public async Task DeleteStudentAsync(int id)
{
var student = await _context.Students.FindAsync(id);
if (student != null)
{
_context.Students.Remove(student);
await _context.SaveChangesAsync();
}
}

Change Tracking

EF tracks changes automatically.

var student = await _context.Students.FindAsync(1);
student.Name = "Updated Name";
// Don't need to call Update()
await _context.SaveChangesAsync();

EF detects property changes and generates SQL UPDATE automatically.

Relationships

One-to-Many: Student → Exams

public class Exam
{
public int Id { get; set; }
public string Name { get; set; }
public int SubjectId { get; set; }

// Navigation property
public Subject Subject { get; set; }
}

public class Subject
{
public int Id { get; set; }
public string Name { get; set; }

// Navigation collection
public List<Exam> Exams { get; set; } = new();
}

Many-to-Many: Student ↔ Subject (through StudentSubject)

public class Student
{
public int Id { get; set; }
public string Name { get; set; }

public List<StudentSubject> StudentSubjects { get; set; } = new();
}

public class Subject
{
public int Id { get; set; }
public string Name { get; set; }

public List<StudentSubject> StudentSubjects { get; set; } = new();
}

public class StudentSubject
{
public int StudentId { get; set; }
public Student Student { get; set; }

public int SubjectId { get; set; }
public Subject Subject { get; set; }
}

Key Takeaways

  • EF Core = object mapping (classes ↔ tables)
  • DbContext = database connection and query interface
  • Code-First = define classes, migrations create schema
  • LINQ = type-safe queries in C#
  • Change tracking = automatic update detection
  • Relationships = navigation properties
  • async/await = non-blocking database operations
💡 EF Core Tip

Use LINQ. Never build SQL strings manually.

🤖Use AI to Learn Faster

Use ChatGPT, Claude, or Copilot to go deeper on Entity Framework Core Fundamentals. Try these prompts:

  • "What's the difference between EF Core and ADO.NET?"
  • "How do migrations work?"
  • "What is change tracking?"
  • "How do I define relationships?"
  • "Quiz me on EF Core"

💡 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.

nexcoding.in