跳到主要内容

Entity Framework Core

What EF Core Is (Interview One-Liner)

EF Core is a lightweight ORM that lets you work with databases using C# objects instead of raw SQL, while still allowing full control when needed.

ORM = Object ↔ Relational Mapper


How EF Core Works (Big Picture)

Think in 3 layers:

C# Entities

EF Core Mapping

Database Tables

Core Flow:

  1. You define entities (C# classes)
  2. DbContext tracks them
  3. You query using LINQ
  4. EF Core translates LINQ → SQL
  5. Database executes SQL
  6. Results → C# objects

Core Building Blocks (Must Memorize)

🔹 Entity

  • A C# class
  • Represents one database table
  • Properties = columns
public class Person
{
public Guid PersonID { get; set; }
public string PersonName { get; set; }
}

🔹 DbContext (VERY IMPORTANT)

DbContext = a session with the database

Responsibilities:

  • Database connection
  • Change tracking
  • LINQ → SQL translation
  • Saving changes
public class PersonsDbContext : DbContext
{
public DbSet<Person> Persons { get; set; }
}

📌 Lifetime: Scoped (one per request)


🔹 DbSet<T>

DbSet = a table

_db.Persons.Add(person);
_db.Persons.Where((p) => p.Name == "John");

EF Core Query Lifecycle (Interview Gold ⭐)

LINQ → Expression Tree → SQL → Database → Objects
  • Queries are not executed immediately
  • Executed only when:
    • ToList()
    • FirstOrDefault()
    • Single()

This is called deferred execution.


Change Tracking (Why EF Core Feels "Magic")

EF Core tracks entity states:

StateMeaning
AddedWill INSERT
ModifiedWill UPDATE
DeletedWill DELETE
UnchangedNo action
_db.Persons.Add(person);
_db.SaveChanges(); // generates SQL

📌 Interview line:

"EF Core detects changes automatically and generates the correct SQL."


CRUD with EF Core (Service Layer)

Create

_db.Persons.Add(person);
await _db.SaveChangesAsync();

Read

await _db.Persons.ToListAsync();

Update

person.Name = "New";
await _db.SaveChangesAsync();

Delete

_db.Persons.Remove(person);
await _db.SaveChangesAsync();

Relationships (VERY COMMON INTERVIEW TOPIC)

Relationship Types

  • One-to-One
  • One-to-Many ⭐
  • Many-to-Many

Example: Country → Persons (1 → Many)

entity
.HasOne((p) => p.Country)
.WithMany((c) => c.Persons)
.HasForeignKey((p) => p.CountryID);
public Country Country { get; set; }

❌ Lazy Loading (avoid)

  • Hidden queries
  • Performance issues

✅ Eager Loading

_db.Persons.Include((p) => p.Country);

📌 Interview rule:

"Only load what you need."


9️⃣ Configuration: Data Annotations vs Fluent API

Data Annotations

  • Simple
  • Close to model
  • Limited
[Required][MaxLength(50)];

Fluent API ⭐ (Preferred)

  • More powerful
  • Clean entities
  • Centralized
modelBuilder.Entity<Person>()
.Property(p => p.Name)
.HasMaxLength(50)
.IsRequired();

📌 Interview answer:

"Fluent API is better for complex rules and separation of concerns."


Code First vs Database First

Code FirstDatabase First
Start from C#Start from DB
MigrationsScaffold
FlexibleLegacy DB

📌 Most modern apps → Code First


Migrations (SUPER IMPORTANT)

Migrations keep your database schema in sync with your code

Common Commands

Add-Migration AddPersonsTable
Update-Database

Each migration:

  • Up() → apply
  • Down() → rollback

📌 Best practice:

  • Small migrations
  • Clear names
  • Review generated SQL

Seed Data

Used for:

  • Lookup tables
  • Demo data
  • Dev/test environments
modelBuilder.Entity<Country>()
.HasData(new Country { CountryID = ..., CountryName = "Japan" });

📌 Interview tip:

"Seed data is versioned via migrations."


Connection Strings

Stored in:

  • appsettings.json
  • Environment variables
  • User secrets
options.UseSqlServer(
builder.Configuration.GetConnectionString("DefaultConnection"),
);

📌 Production → environment variables / Key Vault


Async EF Core (NON-NEGOTIABLE)

Always use:

  • ToListAsync
  • SaveChangesAsync

Why?

  • Scalability
  • Non-blocking threads

📌 Interview line:

"Async EF prevents thread starvation under load."


Stored Procedures (When Needed)

EF Core supports them:

_db.Persons.FromSqlRaw("EXEC GetAllPersons");

Use when:

  • Legacy DB
  • Complex SQL
  • Performance-critical paths

Best Practices (Memorize This List)

✅ Use services (not controllers) ✅ Use async everywhere ✅ Use DTOs ✅ Avoid lazy loading ✅ Use Include carefully ✅ Prefer Fluent API ✅ Small migrations ❌ Don't expose DbContext everywhere


One-Paragraph Interview Answer

"EF Core is a modern ORM that allows us to work with relational data using strongly typed C# objects. It uses DbContext to track changes and translate LINQ queries into SQL. We typically use Code First with migrations to manage schema changes, Fluent API for configuration, async operations for scalability, and services to encapsulate data access logic. EF Core improves productivity while still allowing raw SQL or stored procedures when needed."


If You Remember ONLY 7 Things

  1. DbContext = database session
  2. DbSet = table
  3. LINQ → SQL
  4. Change tracking
  5. Include for relationships
  6. Migrations sync schema
  7. Async everywhere