Часть 4. Razor Страницы с EF Core миграцией в ASP.NET Core

Авторы: Том Дайкстра (Tom Dykstra), Йон П. Смит (Jon P Smith) и Рик Андерсон (Rick Anderson)

Веб-приложение Contoso University демонстрирует создание Razor веб-приложений Pages с помощью EF Core Visual Studio. Сведения о серии руководств см. в первом руководстве серии.

При возникновении проблем, которые вам не удается устранить, скачайте готовое приложение и сравните его код с тем, который вы создали в процессе работы с этим руководством.

В этом руководстве представлена EF Core функция миграции для управления изменениями модели данных.

При разработке нового приложения модель данных часто изменяется. При каждом изменении нарушается синхронизация модели с базой данных. Вы начали работу с этой серией учебников с настройки платформы Entity Framework для создания базы данных, если она еще не существует. При каждом изменении модели данных базу данных необходимо удалять. При следующем запуске приложения вызов EnsureCreated повторно создает базу данных в соответствии с новой моделью данных. Затем выполняется класс DbInitializer для заполнения новой базы данных.

Такой подход к обеспечению синхронизации базы данных с использованием модели данных хорошо работает, пока приложение не нужно развертывать в рабочей среде. Приложение, выполняющееся в рабочей среде, обычно содержит данные. Приложение не может запускаться с тестовой базой данных каждый раз при внесении изменений (например, при добавлении столбца). Функция EF Core миграции решает эту проблему, позволяя EF Core обновить схему базы данных вместо создания новой базы данных.

Вместо удаления и повторного создания базы данных при изменении модели функция миграций обновляет схему, сохраняя существующие данные.

Примечание.

Ограничения SQLite

В этом руководстве используется функция миграции Entity Framework Core, где это возможно. Во время миграции обновляется схема базы данных в соответствии с изменениями в модели данных. Однако миграции могут вносить только такие изменения, которые поддерживает ядро СУБД, а возможности изменения схемы SQLite ограничены. Например, добавление столбца поддерживается, но удаление столбца не поддерживается. Если миграция создается для удаления столбца, команда ef migrations add выполняется успешно, а команда ef database update — нет.

Обходной путь для ограничений SQLite — вручную написать код миграции для перестроения таблицы в случае изменений. Код для миграции находится в методах Up и Down и выполняет следующие действия:

  • Создание новой таблицы.
  • Копирование данных из старой таблицы в новую.
  • Удаление старой таблицы.
  • Переименование новой таблицы.

Написание кода такого рода для конкретной базы данных выходит за рамки данного учебника. Вместо этого в данном учебнике база данных удаляется и создается повторно, когда попытка применить миграцию завершается сбоем. Дополнительные сведения см. на следующих ресурсах:

Удалите базу данных:

Используйте обозреватель объектов SQL Server, чтобы удалить базу данных, или выполните следующую команду в консоли диспетчера пакетов (PMC):

Drop-Database

Создание первоначальной миграции

Выполните следующие команды в PMC:

Add-Migration InitialCreate
Update-Database

Удаление EnsureCreated

Эта серия учебников началась с использования EnsureCreated. Метод EnsureCreated не создает таблицу журнала миграций и поэтому не может использоваться с функцией миграций. Он предназначен для тестирования или быстрого создания прототипов, когда часто требуется удалять и повторно создавать базу данных.

Начиная с этого момента в учебниках будут использоваться миграции.

Удалите Program.csследующую строку:

context.Database.EnsureCreated();

Запустите приложение и убедитесь в том, что база данных заполняется.

Методы Up и Down

Команда EF Coremigrations add создала код для создания базы данных. Код миграций содержится в файле Migrations\<timestamp>_InitialCreate.cs. Метод Up класса InitialCreate создает таблицы базы данных, соответствующие наборам сущностей модели данных. Метод Down удаляет их, как показано в следующем примере:

using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;

namespace ContosoUniversity.Migrations
{
    public partial class InitialCreate : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "Course",
                columns: table => new
                {
                    CourseID = table.Column<int>(nullable: false),
                    Title = table.Column<string>(nullable: true),
                    Credits = table.Column<int>(nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Course", x => x.CourseID);
                });

            migrationBuilder.CreateTable(
                name: "Student",
                columns: table => new
                {
                    ID = table.Column<int>(nullable: false)
                        .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
                    LastName = table.Column<string>(nullable: true),
                    FirstMidName = table.Column<string>(nullable: true),
                    EnrollmentDate = table.Column<DateTime>(nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Student", x => x.ID);
                });

            migrationBuilder.CreateTable(
                name: "Enrollment",
                columns: table => new
                {
                    EnrollmentID = table.Column<int>(nullable: false)
                        .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
                    CourseID = table.Column<int>(nullable: false),
                    StudentID = table.Column<int>(nullable: false),
                    Grade = table.Column<int>(nullable: true)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Enrollment", x => x.EnrollmentID);
                    table.ForeignKey(
                        name: "FK_Enrollment_Course_CourseID",
                        column: x => x.CourseID,
                        principalTable: "Course",
                        principalColumn: "CourseID",
                        onDelete: ReferentialAction.Cascade);
                    table.ForeignKey(
                        name: "FK_Enrollment_Student_StudentID",
                        column: x => x.StudentID,
                        principalTable: "Student",
                        principalColumn: "ID",
                        onDelete: ReferentialAction.Cascade);
                });

            migrationBuilder.CreateIndex(
                name: "IX_Enrollment_CourseID",
                table: "Enrollment",
                column: "CourseID");

            migrationBuilder.CreateIndex(
                name: "IX_Enrollment_StudentID",
                table: "Enrollment",
                column: "StudentID");
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropTable(
                name: "Enrollment");

            migrationBuilder.DropTable(
                name: "Course");

            migrationBuilder.DropTable(
                name: "Student");
        }
    }
}

Приведенный выше код предназначен для первоначальной миграции. Код.

  • был создан командой migrations add InitialCreate;
  • выполняется командой database update;
  • создает базу данных для модели данных, заданной классом контекста базы данных.

Параметр имени миграции (в примере это InitialCreate) используется в качестве имени файла. В качестве имени миграции можно использовать любое допустимое имя файла. Рекомендуется выбрать слово или фразу, которые кратко описывают назначение миграции. Например, миграция, обеспечивающая добавление таблицы кафедр, может называться "AddDepartmentTable".

Таблица журнала миграции

  • Используйте SSOX или средство SQLite для просмотра базы данных.
  • Обратите внимание на добавление таблицы __EFMigrationsHistory. Таблица __EFMigrationsHistory используется для отслеживания миграций, которые были применены к базе данных.
  • Просмотрите данные в таблице __EFMigrationsHistory. Вы увидите одну строку для первой миграции.

Моментальный снимок модели данных

Миграции создают моментальный снимок текущей модели данных.Migrations/SchoolContextModelSnapshot.cs После добавления миграции EF определяет изменения, сравнивая текущую модель данных с файлом моментального снимка.

Так как файл моментального снимка отслеживает состояние модели данных, миграцию нельзя удалить, удалив файл <timestamp>_<migrationname>.cs. Чтобы отменить последнюю миграцию, используйте команду migrations remove. migrations remove удаляет миграцию и гарантирует корректный сброс моментального снимка. Дополнительные сведения см. в разделе dotnet ef migrations remove.

См. дополнительные сведения в статье Сброс всех миграций.

Применение миграций в рабочей среде

Для рабочих приложений не рекомендуется вызывать Database.Migrate при запуске приложения. Migrate не следует вызывать из приложения, развернутого в ферме серверов. Если приложение масштабируется в нескольких экземплярах сервера, трудно гарантировать, что изменения схемы базы данных не будут выполняться с нескольких серверов и не будут конфликтовать с обращениями для чтения или записи.

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

  • Использование миграций для создания сценариев SQL и их использования в развертывании.
  • Выполнение dotnet ef database update из контролируемой среды.

Устранение неполадок

Если приложение использует SQL Server LocalDB и выводит следующее исключение:

SqlException: Cannot open database "ContosoUniversity" requested by the login.
The login failed.
Login failed for user 'user name'.

решением может быть выполнение dotnet ef database update из командной строки.

Дополнительные ресурсы

Следующие шаги

В следующем руководстве продолжается построение модели данных путем добавления свойств сущностей и новых сущностей.

В этом руководстве EF Core используется функция миграции для управления изменениями модели данных.

При возникновении проблем, которые вам не удается устранить, скачайте готовое приложение.

При разработке нового приложения модель данных часто изменяется. При каждом изменении нарушается синхронизация модели с базой данных. Вы начали работу с этим учебником, настроив платформу Entity Framework для создания базы данных, если она еще не существует. Каждый раз при изменении модели данных:

  • База данных удаляется.
  • Платформа EF создает новую базу данных, соответствующую модели.
  • Приложение заполняет базу тестовыми данными.

Такой подход к обеспечению синхронизации базы данных с использованием модели данных хорошо работает, пока приложение не нужно развертывать в рабочей среде. Приложение, выполняющееся в рабочей среде, обычно содержит данные. Приложение не может запускаться с тестовой базой данных каждый раз при внесении изменений (например, при добавлении столбца). Функция EF Core миграции решает эту проблему, позволяя EF Core обновить схему базы данных вместо создания новой базы данных.

Вместо удаления и повторного создания базы данных при изменении модели функция миграций обновляет схему, сохраняя существующие данные.

Удалите базу данных:

Воспользуйтесь обозревателем объектов SQL Server (SSOX) или командой database drop:

Выполните следующие команды в консоли диспетчера пакетов (PMC):

Drop-Database

Чтобы просмотреть справочную информацию, выполните команду Get-Help about_EntityFrameworkCore в PMC.

Создание первоначальной миграции и обновление базы данных

Выполните построение проекта и создайте первую миграцию.

Add-Migration InitialCreate
Update-Database

Обзор методов Up и Down

Команда EF Coremigrations add создала код для создания базы данных. Код миграций содержится в файле Migrations\<timestamp>_InitialCreate.cs. Метод Up класса InitialCreate создает таблицы базы данных, соответствующие наборам сущностей модели данных. Метод Down удаляет их, как показано в следующем примере:

public partial class InitialCreate : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.CreateTable(
            name: "Course",
            columns: table => new
            {
                CourseID = table.Column<int>(nullable: false),
                Title = table.Column<string>(nullable: true),
                Credits = table.Column<int>(nullable: false)
            },
            constraints: table =>
            {
                table.PrimaryKey("PK_Course", x => x.CourseID);
            });

        migrationBuilder.CreateTable(
    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DropTable(
            name: "Enrollment");

        migrationBuilder.DropTable(
            name: "Course");

        migrationBuilder.DropTable(
            name: "Student");
    }
}

Функция миграций вызывает метод Up, чтобы реализовать изменения модели данных для миграции. При вводе команды для отката обновления функция миграций вызывает метод Down.

Приведенный выше код предназначен для первоначальной миграции. Этот код был создан при выполнении команды migrations add InitialCreate. Параметр имени миграции (в примере это "InitialCreate") используется в качестве имени файла. В качестве имени миграции можно использовать любое допустимое имя файла. Рекомендуется выбрать слово или фразу, которые кратко описывают назначение миграции. Например, миграция, обеспечивающая добавление таблицы кафедр, может называться "AddDepartmentTable".

Если создается первоначальная миграция и база данных существует:

  • Формируется код создания базы данных.
  • Выполнять код создания базы данных не нужно, поскольку база уже соответствует модели данных. В случае выполнения код создания базы данных не вносит никаких изменений, поскольку база уже соответствует модели данных.

При развертывании приложения в новой среде этот код необходимо выполнить для создания новой базы данных.

Предыдущая база данных была удалена и не существует, поэтому функция миграций создает новую базу данных.

Моментальный снимок модели данных

Миграции создают моментальный снимок текущей схемы базы данных.Migrations/SchoolContextModelSnapshot.cs При добавлении миграции EF определяет, что именно изменилось, сравнивая модель данных с файлом моментального снимка.

Чтобы удалить миграцию, выполните следующую команду:

Remove-Migration

Команда remove migrations удаляет миграцию и гарантирует корректный сброс моментального снимка.

Удаление EnsureCreated и тестирование приложения

Для ранней разработки использовалась команда EnsureCreated. В этом учебнике используются миграции. EnsureCreated имеет следующие ограничения:

  • Пропускает миграции и создает базу данных и схему.
  • Не создает таблицу миграций.
  • Не может использоваться с миграциями.
  • Разработана для тестирования или быстрого создания прототипов, когда часто требуется удаление и повторное создание базы данных.

Удалите EnsureCreated:

context.Database.EnsureCreated();

Запустите приложение и убедитесь, что база заполняется данными.

Проверка базы данных

Используйте обозреватель объектов SQL Server для проверки базы данных. Обратите внимание на добавление таблицы __EFMigrationsHistory. Таблица __EFMigrationsHistory используется для отслеживания миграций, которые были применены к базе данных. Просмотрев данные в таблице __EFMigrationsHistory, вы увидите одну строку для первой миграции. Последний журнал в предыдущем примере выходных данных интерфейса командной строки показывает инструкцию INSERT, создающую эту строку.

Запустите приложение и убедитесь, что все функции работают.

Применение миграций в рабочей среде

Для рабочих приложений не рекомендуется вызывать Database.Migrate при запуске приложения. Migrate не следует вызывать из приложения в ферме серверов. Например, если приложение было развернуто в облаке с горизонтальным масштабированием (выполняется несколько экземпляров приложения).

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

  • Использование миграций для создания сценариев SQL и их использования в развертывании.
  • Выполнение dotnet ef database update из контролируемой среды.

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

Устранение неполадок

Скачайте готовое приложение.

Приложение создает следующее исключение:

SqlException: Cannot open database "ContosoUniversity" requested by the login.
The login failed.
Login failed for user 'user name'.

Решение. Выполните dotnet ef database update

Дополнительные ресурсы