.NET Tutorial
Junior5 мин чтенияВопрос 3 из 14

Code First vs Database First vs Model First?

Направление синхронизации

Code First

dotnet ef migrations add

dotnet ef database update

C#-классы + Fluent API

Миграции (C#)

База данных

Database First

dotnet ef dbcontext scaffold

Существующая БД

C#-классы + DbContext

Model First (EF6, устарел)

Диаграмма EDMX

C#-классы

DDL-скрипт

Code First

dotnet ef migrations add

dotnet ef database update

C#-классы + Fluent API

Миграции (C#)

База данных

Database First

dotnet ef dbcontext scaffold

Существующая БД

C#-классы + DbContext

Model First (EF6, устарел)

Диаграмма EDMX

C#-классы

DDL-скрипт

Что является источником истины

Code First

Модель в коде
public sealed class Product
{
    public int Id { get; private set; }
    public string Name { get; private set; } = "";
    public decimal Price { get; private set; }
    public int CategoryId { get; private set; }
    public Category Category { get; private set; } = null!;
}

public sealed class ProductConfiguration : IEntityTypeConfiguration<Product>
{
    public void Configure(EntityTypeBuilder<Product> b)
    {
        b.ToTable("products");
        b.Property(p => p.Name).HasMaxLength(200).IsRequired();
        b.Property(p => p.Price).HasPrecision(18, 2);
        b.HasIndex(p => p.Name);
        b.HasOne(p => p.Category).WithMany(c => c.Products).HasForeignKey(p => p.CategoryId);
    }
}
Рабочий цикл
dotnet ef migrations add AddProductIndex      # сравнить модель со снапшотом, сгенерировать миграцию
dotnet ef migrations script --idempotent      # SQL-скрипт для ревью и деплоя
dotnet ef database update                     # применить к локальной БД

Плюсы: модель и схема версионируются вместе с кодом в git, ревьюятся в pull request, одинаково разворачиваются на всех окружениях; удобно для DDD (богатая доменная модель, value objects, owned types).

Минусы: нужна дисциплина с миграциями; сложные объекты БД (триггеры, процедуры, партиции) описываются через raw SQL в миграциях; DBA может не понравиться, что схему «генерирует ORM».

Database First

Shell
dotnet ef dbcontext scaffold "Host=db;Database=erp;Username=reader;Password=***" Npgsql.EntityFrameworkCore.PostgreSQL \
  --output-dir Entities --context-dir Data --context ErpDbContext \
  --table public.orders --table public.customers \
  --no-onconfiguring --use-database-names

Генерирует partial-классы сущностей и DbContext с Fluent API. При изменении схемы — повторный scaffold с --force (ручные правки в сгенерированных файлах затрутся — расширяйте через partial-классы).

Плюсы: работа с уже существующей БД; схемой управляют DBA и отдельные инструменты (Flyway, Liquibase, SSDT, DbUp); видно реальную структуру.

Минусы: модель «анемичная» и повторяет таблицы; сложнее применять DDD; нужно регенерировать код при изменениях схемы.

Сравнение

Code First Database First Model First
Источник истины Код База данных Визуальная модель
Изменение схемы Миграции EF Скрипты/инструменты DBA + re-scaffold Дизайнер
Поддержка в EF Core Да Да (scaffold) Нет
Новый проект Идеально Если БД проектируется отдельно —
Legacy БД Сложно (нужна baseline-миграция) Идеально —
DDD, богатая модель Удобно Сложнее —

Как выбрать

нет

да

наша команда,
готовы вести миграции в EF

DBA / другая команда /
общая БД

База уже существует?

Code First + миграции

Кто владеет схемой?

Scaffold один раз →
дальше Code First
(baseline-миграция)

Database First:
scaffold при изменениях

нет

да

наша команда,
готовы вести миграции в EF

DBA / другая команда /
общая БД

База уже существует?

Code First + миграции

Кто владеет схемой?

Scaffold один раз →
дальше Code First
(baseline-миграция)

Database First:
scaffold при изменениях

Выбор подхода

Переход Database First → Code First

  1. Scaffold текущей схемы.
  2. dotnet ef migrations add Baseline.
  3. Очистить методы Up/Down миграции (схема уже существует) или применить её только к новым окружениям, а в существующей БД вручную вставить запись в __EFMigrationsHistory.
  4. Дальше — обычный Code First.

Вопросы на засыпку

Можно ли в Code First использовать хранимые процедуры, представления, триггеры?

Да: создавать их через migrationBuilder.Sql("CREATE VIEW ...") в миграциях, маппить представления на сущности без ключа (ToView("..."), HasNoKey()), вызывать процедуры через FromSql/ExecuteSql. EF не отслеживает их изменения автоматически — это ручная работа в миграциях.

Что делать, если в Database First нужно добавить в сущность вычисляемое свойство?

Сгенерированные классы — partial. Создайте отдельный файл с partial class Order и добавьте свойство (с [NotMapped] или конфигурацией Ignore) — при повторном scaffold оно не пропадёт.

Можно ли использовать EF Core без миграций при Code First?

db.Database.EnsureCreated() создаёт схему по модели без миграций — удобно для тестов и прототипов, но несовместимо с последующими миграциями и не умеет изменять существующую схему.

Почему Model First исчез?

EDMX-дизайнер был тяжёлым, плохо работал с системами контроля версий (огромный XML с конфликтами при слиянии) и с кроссплатформенностью. EF Core сделал ставку на модель в коде; для визуализации есть EF Core Power Tools (диаграммы DGML из модели).

Итог

Code First — выбор по умолчанию для новых проектов: модель в коде, схема в миграциях, всё в git. Database First — для существующих баз и схем, которыми управляют не разработчики приложения. Model First остался в EF6. Часто оптимален гибрид: scaffold один раз, дальше — Code First.