Introduction
ShopNest Academy (School Management) connects Students, Courses, and Teachers through EF Core relationships. Modeling them correctly avoids duplicate data, orphaned records, and slow queries.
After this article you will
- Configure one-to-one, one-to-many, and many-to-many
- Use navigation properties both directions
- Apply Fluent API for cascade delete rules
- Choose eager, lazy, or explicit loading
- Detect and fix N+1 query problems
Prerequisites
- Article 16 — EF Core LINQ Queries
- ShopNest DbContext with Products/Orders from Articles 13–15
- SQL Server or LocalDB configured
Concept deep-dive
One-to-one
Student → StudentProfile (one profile per student). FK on dependent side.
public class Student
{
public int Id { get; set; }
public StudentProfile Profile { get; set; }
}
public class StudentProfile
{
public int Id { get; set; }
public int StudentId { get; set; }
public Student Student { get; set; }
}
One-to-many
Teacher → many Courses. FK TeacherId on Course.
Many-to-many (.NET 6+)
Students ↔ Courses via implicit join table, or explicit Enrollment entity with Grade and EnrolledAt.
public class Enrollment
{
public int StudentId { get; set; }
public Student Student { get; set; }
public int CourseId { get; set; }
public Course Course { get; set; }
public decimal? Grade { get; set; }
}
// Fluent API
modelBuilder.Entity<Enrollment>()
.HasKey(e => new { e.StudentId, e.CourseId });
Cascade delete
OnDelete(DeleteBehavior.Cascade) — deleting Teacher deletes Courses? Usually Restrict on business FKs to prevent accidents. Cascade OK for owned child rows (OrderLines).
Loading strategies
| Strategy | How | Risk |
|---|---|---|
| Eager | .Include() | Large joins if over-include |
| Lazy | Virtual + proxy | N+1 in loops |
| Explicit | context.Entry().Collection().Load() | Verbose but controlled |
Hands-on — ShopNest School Management System
- Scaffold Student, Course, Teacher, Enrollment entities.
- Migration with FK constraints and Restrict on Teacher→Course.
- Student details page: Include Profile + Enrollments.ThenInclude(Course).
- Log SQL — verify single query vs N+1.
Common errors & best practices
- Multiple cascade paths in SQL Server — use Restrict on one relationship.
- Missing inverse navigation — EF can still work but Fluent config is clearer.
- Many-to-many without join entity when you need extra columns on enrollment.
Interview questions
Q: N+1 problem?
A: 1 query for list + N queries for related data — fix with Include or projection.
Q: When explicit join entity?
A: When relationship has payload (Grade, EnrolledAt) or audit fields.
Q: Cascade delete safe?
A: Rarely on master data; OK for dependent detail rows owned by parent.
Summary
- Model relationships with navigation properties + FKs
- Fluent API controls cascade and join tables
- Include eagerly; avoid lazy loading in ASP.NET request paths
- Enrollment join entity for Student-Course grades
Previous: EF Core LINQ Queries
Next: EF Core Fluent API
FAQ
Does EF Core 8 still need join entity for many-to-many?
Optional — implicit join table works for simple M:N; use explicit entity when you need extra columns.
How detect N+1?
Enable sensitive data logging; count SQL statements per HTTP request.