Ef Core

10.8. Міграції (Migrations)

10.8. Міграції (Migrations)

Вступ: Еволюція бази даних

Код змінюється — і база даних повинна змінюватися разом з ним. Коли ви додаєте нову властивість до класу Book, відповідний стовпець повинен з'явитися в таблиці. Коли змінюєте тип із string на int — стовпець має змінити тип. Міграції EF Core — це механізм, який переводить вашу C# модель у SQL-команди для оновлення бази даних.

Передумови: 10.3. Entity Configuration. Встановлений dotnet-ef CLI.

Code-First vs Database-First

ПідхідХто головнийІнструментКоли
Code-FirstC#-класи → БДMigrationsНові проєкти
Database-FirstБД → C#-класиScaffoldLegacy БД

Code-First (рекомендований)

Ви пишете C#-класи, EF Core генерує SQL:

C# Model → Migration File → SQL Script → Database

Database-First

Існуюча БД, EF Core генерує C#-класи:

dotnet ef dbcontext scaffold "Server=...;Database=LibraryDb;..." Microsoft.EntityFrameworkCore.SqlServer

Ми зосередимось на Code-First, оскільки це стандарт для нових проєктів.


Робочий цикл міграцій

Створення міграції

# Після зміни моделі
dotnet ef migrations add AddBookPageCount

EF Core:

  1. Аналізує поточну модель (C#-класи та DbContext)
  2. Порівнює з попереднім snapshot
  3. Генерує Migration file з Up() та Down()

Застосування міграції

# Оновити БД до останньої міграції
dotnet ef database update

# Оновити до конкретної міграції
dotnet ef database update AddBookPageCount

# Відкотити до попередньої
dotnet ef database update PreviousMigrationName

Видалення міграції

# Видалити останню (ще не застосовану) міграцію
dotnet ef migrations remove

Структура файлу міграції

public partial class AddBookPageCount : Migration
{
    // Up — що зробити при оновленні
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.AddColumn<int>(
            name: "PageCount",
            table: "Books",
            type: "int",
            nullable: false,
            defaultValue: 0);
    }

    // Down — що зробити при відкаті
    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DropColumn(
            name: "PageCount",
            table: "Books");
    }
}

Розбір:

  • Up() — SQL для оновлення бази до цієї версії. ALTER TABLE Books ADD PageCount INT NOT NULL DEFAULT 0.
  • Down() — SQL для відкату. ALTER TABLE Books DROP COLUMN PageCount.

EF Core також генерує snapshot-файл (LibraryContextModelSnapshot.cs), який зберігає поточний стан моделі.


Data Seeding (початкові дані)

HasData у OnModelCreating

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Author>().HasData(
        new Author { Id = 1, Name = "Роберт Мартін", Country = "США" },
        new Author { Id = 2, Name = "Мартін Фаулер", Country = "Великобританія" },
        new Author { Id = 3, Name = "Ерік Еванс", Country = "США" }
    );

    modelBuilder.Entity<Book>().HasData(
        new { Id = 1, Title = "Чистий код", Year = 2008, Isbn = "ISBN-1", AuthorId = 1, IsAvailable = true },
        new { Id = 2, Title = "Рефакторинг", Year = 1999, Isbn = "ISBN-2", AuthorId = 2, IsAvailable = true },
        new { Id = 3, Title = "DDD", Year = 2003, Isbn = "ISBN-3", AuthorId = 3, IsAvailable = true }
    );
}
HasData вимагає явного Id. Це тому, що seed дані включаються в міграцію як INSERT з конкретними Id. EF Core перевіряє, чи не змінилися seed дані, при кожній новій міграції.

Окремий Seeder

Для складніших даних — окремий клас:

public static class DatabaseSeeder
{
    public static void Seed(LibraryContext context)
    {
        if (context.Authors.Any()) return; // Вже заповнено

        var author = new Author { Name = "Роберт Мартін", Country = "США" };
        context.Authors.Add(author);
        context.SaveChanges();

        context.Books.AddRange(
            new Book { Title = "Чистий код", Year = 2008, Isbn = "ISBN-1", AuthorId = author.Id },
            new Book { Title = "Clean Architecture", Year = 2017, Isbn = "ISBN-2", AuthorId = author.Id }
        );
        context.SaveChanges();
    }
}

Типові операції міграцій

// У методі Up() можна додавати кастомний SQL:
protected override void Up(MigrationBuilder migrationBuilder)
{
    // Стандартні операції EF Core
    migrationBuilder.CreateTable(...);
    migrationBuilder.AddColumn(...);
    migrationBuilder.AlterColumn(...);
    migrationBuilder.DropColumn(...);
    migrationBuilder.CreateIndex(...);
    migrationBuilder.AddForeignKey(...);
    migrationBuilder.RenameColumn(...);

    // Кастомний SQL
    migrationBuilder.Sql("UPDATE Books SET IsAvailable = 1 WHERE IsAvailable IS NULL");
    migrationBuilder.Sql("CREATE VIEW ActiveBooks AS SELECT * FROM Books WHERE IsDeleted = 0");
}

Міграції у CI/CD

Генерація SQL-скрипту

# Ідемпотентний скрипт (безпечний для повторного запуску)
dotnet ef migrations script --idempotent -o migration.sql

Цей скрипт перевіряє таблицю __EFMigrationsHistory і виконує тільки нові міграції.

Migration Bundle (EF Core 6+)

# Створити виконуваний файл для міграцій
dotnet ef migrations bundle --self-contained -r linux-x64

# Запустити на сервері
./efbundle --connection "Server=prod;Database=LibraryDb;..."

Практичні завдання

Рівень 1: Базовий

Завдання 1.1: Перша міграція

  1. Створіть модель Product (Name, Price, Category).
  2. dotnet ef migrations add InitialCreate.
  3. Подивіться згенерований файл.
  4. dotnet ef database update.

Завдання 1.2: Еволюція

  1. Додайте Description до Product.
  2. Створіть міграцію AddProductDescription.
  3. Перевірте Up() та Down().
  4. Відкотіть міграцію, перевірте БД.

Рівень 2: Практичний

Завдання 2.1: Data Seeding

  1. Додайте HasData з 5 категоріями та 10 продуктами.
  2. Створіть міграцію, перевірте SQL.
  3. Змініть один seed — створіть нову міграцію, перевірте різницю.

Завдання 2.2: Кастомний SQL

  1. Додайте в міграцію створення VIEW.
  2. Додайте INSERT для довідникових даних.
  3. Згенеруйте ідемпотентний SQL-скрипт.

Резюме

Code-First

C#-класи → міграції → SQL → БД. Стандарт для нових проєктів.

Migration File

Up() — оновлення, Down() — відкат. Snapshot зберігає поточний стан моделі.

Data Seeding

HasData з явними Id для довідників. DatabaseSeeder для складних тестових даних.

CI/CD

dotnet ef migrations script --idempotent для продакшн. Migration Bundle для автономного застосування.
Copyright © 2026