В чём разница между подходами Code-First, Database-First и Model-First?
Code-First: модель в C#-классах, схема БД через миграции. Database-First: scaffold-команда генерирует классы из существующей БД. Model-First (только EF 6): устаревший визуальный дизайнер EDMX, в EF Core не поддерживается.
Три подхода к моделированию в Entity Framework
Entity Framework поддерживает три стратегии определения модели данных. Выбор зависит от того, что является источником истины: код, существующая БД или визуальная диаграмма.
Code-First
Модель описывается C#-классами (POCO). EF Core генерирует схему БД через миграции. Это единственный подход, который полноценно поддерживается в EF Core (EF 6 поддерживал все три).
// 1. Определяем классы
public class Order
{
public int Id { get; set; }
public DateTime CreatedAt { get; set; }
public string Status { get; set; } = "pending";
public ICollection<OrderItem> Items { get; set; } = new List<OrderItem>();
}
public class OrderItem
{
public int Id { get; set; }
public int OrderId { get; set; }
public Order Order { get; set; } = null!;
public string ProductName { get; set; } = string.Empty;
public decimal Price { get; set; }
}
// 2. Конфигурация через Fluent API
public class AppDbContext : DbContext
{
public DbSet<Order> Orders => Set<Order>();
public DbSet<OrderItem> OrderItems => Set<OrderItem>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Order>()
.Property(o => o.Status)
.HasMaxLength(50)
.HasDefaultValue("pending");
modelBuilder.Entity<OrderItem>()
.Property(i => i.Price)
.HasColumnType("decimal(18,2)");
}
}
// 3. Создаём и применяем миграцию
// dotnet ef migrations add InitOrders
// dotnet ef database update
Database-First
Источник истины — существующая база данных. EF Core генерирует классы и DbContext с помощью команды scaffold (reverse engineering).
# Scaffolding из существующей БД (SQL Server)
dotnet ef dbcontext scaffold \
"Server=.;Database=Shop;Trusted_Connection=True" \
Microsoft.EntityFrameworkCore.SqlServer \
--output-dir Models \
--context ShopContext \
--data-annotations \
--force
# PostgreSQL
dotnet ef dbcontext scaffold \
"Host=localhost;Database=shop;Username=postgres" \
Npgsql.EntityFrameworkCore.PostgreSQL \
--output-dir Models
Сгенерированные файлы не следует редактировать вручную — при повторном scaffolding они перезаписываются. Кастомизацию выносят в partial-классы.
Model-First (только EF 6)
Модель рисуется в визуальном дизайнере Visual Studio (.edmx-файл), из которого генерируются и классы, и схема БД. В EF Core этот подход не поддерживается — Microsoft считает его устаревшим и рекомендует Code-First.
Сравнительная таблица
- Code-First: источник истины — C#-код; подходит для новых проектов; версионируется через миграции; поддерживается в EF Core.
- Database-First: источник истины — SQL-схема; подходит для легаси-БД; код генерируется scaffolding-командой; поддерживается в EF Core.
- Model-First: источник истины — EDMX-диаграмма; устарел; только EF 6; в EF Core отсутствует.
Подводные камни
- При Database-First изменения схемы БД не попадают в модель автоматически — нужно повторно запускать
scaffoldс флагом--force, что перезаписывает кастомизации. - Code-First и Database-First нельзя смешивать для одной таблицы: это приведёт к конфликтам между ModelSnapshot и реальной схемой.
- При scaffolding EF Core не всегда корректно распознаёт составные PK, check-constraints и специфичные для диалекта типы — требуется ручная доработка.
- Model-First (EDMX) в EF 6 генерировал тяжёлый XML-файл с плохим разрешением конфликтов в git — одна из причин отказа от него в EF Core.
- Code-First без миграций (
EnsureCreated) не обновляет существующую схему — подходит только для тестов или первого запуска. - Database-First не генерирует историю миграций — при переходе на Code-First нужно вручную создать baseline-миграцию командой
migrations add --ignore-changes.
Common mistakes
- Путать Code-First, Database-First и исторический Model-First с похожим механизмом из другой версии или платформы.
- Игнорировать runtime-границы Entity Framework: lifecycle, DI scope, SQL translation, UI thread или platform API.
- Не обсуждать null/empty/error cases и поведение под нагрузкой.
What the interviewer is testing
- Кандидат объясняет Code-First, Database-First и исторический Model-First на конкретном примере, а не только определением.
- Указывает последствия для производительности, тестируемости и поддержки.
- Различает документированное поведение текущего стека и устаревшие практики.