Работали ли вы с миграциями базы данных в Entity Framework Core?

Ответ

Да, активно использовал миграции EF Core для управления эволюцией схемы базы данных в процессе разработки и развертывания.

Базовый рабочий процесс:

# 1. Создание миграции после изменения модели
dotnet ef migrations add AddCustomerEmailColumn
# 2. Обновление базы данных до последней миграции
dotnet ef database update
# 3. Генерация SQL-скрипта для применения в продакшене
dotnet ef migrations script --idempotent --output migration.sql

Ключевые практики и нюансы:

  1. Структура миграции: Каждая миграция содержит два основных метода в файле <MigrationName>.Designer.cs:

    • Up(): Применяет изменения.
    • Down(): Откатывает изменения.
  2. Кастомизация миграций: Автоматически сгенерированный код часто требует ручной доработки.

    // В методе Up() сгенерированной миграции
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.AddColumn<string>(
            name: "Email",
            table: "Customers",
            type: "nvarchar(200)",
            maxLength: 200,
            nullable: false,
            defaultValue: ""); // Добавляем значение по умолчанию для существующих строк
        // Можно добавлять индексы, внешние ключи, выполнять сырые SQL
        migrationBuilder.Sql("UPDATE Customers SET Email = 'temp@example.com' WHERE Email IS NULL");
    }
  3. Безопасное развертывание в продакшене:

    • Никогда не запускать dotnet ef database update напрямую на продакшен-сервере.
    • Использовать SQL-скрипты: Генерировать скрипт (dotnet ef migrations script) и применять его через инструменты CI/CD (например, Azure DevOps, GitHub Actions) или DBA.
    • Идемпотентные скрипты: Флаг --idempotent генерирует скрипт, который можно безопасно запускать многократно.
  4. Работа в команде:

    • Файл миграций (.cs) должен быть закоммичен в систему контроля версий.
    • При конфликтах (два разработчика создали миграции с разными именами) может потребоваться сброс (dotnet ef migrations remove) и пересоздание миграции на основе актуального состояния.
  5. Откат (Rollback):

    # Откат на одну миграцию назад
    dotnet ef database update PreviousMigrationName
    # Полный откат всех миграций (осторожно с данными!)
    dotnet ef database update 0
  6. Пустые миграции: Иногда создаются для выполнения специфического SQL (например, обновления справочных данных, создания сложных индексов).

    dotnet ef migrations add UpdatePostalCodes --empty

Ответ 18+ 🔞

А, миграции в EF Core, да? Ну, это ж классика, как водка с пивом — вроде и работает, но потом голова болит, если не умеешь правильно.

Смотри, я ими пользуюсь постоянно, когда базу надо менять на ходу. Процесс обычно такой, простой, как три копейки:

# 1. Накосячил в моделях — создаёшь миграцию
dotnet ef migrations add AddCustomerEmailColumn
# 2. Локально себе в базу это дело загоняешь
dotnet ef database update
# 3. А для прода — скрипт готовишь, чтобы админы не орали
dotnet ef migrations script --idempotent --output migration.sql

А теперь по пунктам, где собака зарыта и где можно на грабли наступить:

  1. Что внутри. Каждая миграция — это два метода, верх и низ. Up() — чтобы вперёд ехать, применить изменения. Down() — чтобы назад, если всё пошло по пизде и надо откатиться. Всё это в файле с дизайнером лежит.

  2. Ручная работа. Автомат — он тупой, как пробка. Нагенерирует код, а потом смотришь — а он тебе колонку NOT NULL без дефолта суёт в таблицу, где уже данные есть. Ёб твою мать, ну кто так делает? Приходится руками допиливать.

    protected override void Up(MigrationBuilder migrationBuilder)
    {
        // Автомат так сделал бы, и скрипт бы упал на продакшене
        // migrationBuilder.AddColumn<string>(name: "Email", table: "Customers", nullable: false);
    
        // А умный человек сделает вот так:
        migrationBuilder.AddColumn<string>(
            name: "Email",
            table: "Customers",
            type: "nvarchar(200)",
            maxLength: 200,
            nullable: false,
            defaultValue: ""); // Вот этот дефолт — спасение от криков админов
        // И ещё кастомный SQL на всякий пожарный
        migrationBuilder.Sql("UPDATE Customers SET Email = 'temp@example.com' WHERE Email IS NULL OR Email = ''");
    }
  3. Продакшен — это святое. Запомни раз и навсегда, как «Отче наш»: НИКОГДА, БЛЯДЬ, НЕ ГОНЯЙ dotnet ef database update НА БОЕВОЙ БАЗЕ. Тебя просто убьют. Всё через скрипты. Флаг --idempotent — твой лучший друг, он делает скрипт, который можно хоть сто раз запустить, и хуйня не случится. Отдаёшь это дело в CI/CD или суровому дяде DBA, и спишь спокойно.

  4. Командная разработка. Тут весело. Все файлы миграций (.cs) — в гит, это закон. А потом случается: ты сделал миграцию AddEmail, а твой коллега в параллельной ветке тоже сделал миграцию AddPhone. Мержите вы это, а в базе уже есть одна из них. Получается пиздец и конфликт. Часто проще старую миграцию нахуй удалить (dotnet ef migrations remove) и новую, общую, создать на актуальных моделях.

  5. Откатиться, если обосрался. Бывает же.

    # Откатиться на одну ступеньку назад
    dotnet ef database update PreviousMigrationName
    # Откатиться в полный ноль (осторожно, данные могут пострадать!)
    dotnet ef database update 0
  6. Пустые миграции — мощный инструмент. Иногда надо не структуру менять, а данные поправить, или сложный индекс накатить, который EF Core сам не додумается создать. Для этого и нужна пустая миграция.

    dotnet ef migrations add UpdatePostalCodes --empty

    Откроешь файл, и там в Up() пишешь свой кастомный SQL, какой душе угодно. Красота.

В общем, инструмент годный, но с мозгом им пользоваться надо, а не тыкать кнопки наугад. Иначе будет тебе не миграция, а натуральная эмиграция с проекта под крики «кто это сделал?!».