Рекомендуемый способ очистки старых миграций Entity Framework Core
После некоторой разработки нашего приложения мы накопили немало миграций базы данных EFCore. Поскольку EFCore добавляет моментальный снимок всей модели базы данных к каждой миграции, этот код добавляет довольно много. После анализа около 80% нашего времени компиляции тратится на миграцию (компиляция + анализаторы Roslyn).
Итак, пришло время очистить некоторые старые миграции! Но какой лучший способ сделать это? Там, кажется, нет никаких официальных указаний на это...
Нам не нужны откаты (мы только катимся вперед), поэтому все становится проще. Нам нужно поддерживать создание базы данных с нуля и обновление базы после нескольких последних миграций.
Что я пробовал:
Ядерный вариант, по-видимому, заключается в удалении всех миграций и снимка модели и создании новой начальной миграции. Хотя это нормально, это кажется немного опасным. При таком подходе мы должны быть очень осторожны, чтобы каждая часть схемы базы данных была частью модели кода. Например, мы столкнулись с одним крайним случаем: EFCore еще не поддерживает проверенные ограничения. Поэтому мы добавили проверенное ограничение в миграцию, но не в модель кода. Таким образом, при создании новой начальной миграции проверяемое ограничение не было частью этого.
В качестве эксперимента я попытался удалить снимок модели из всех старых миграций, поскольку снимки составляют 90% кода, что приводит к длительному времени компиляции. Я выяснил, что EFCore использует снимок только в качестве инструмента сравнения для новой миграции. После удаления снимка старые миграции, однако, больше не выполнялись при запуске в новой базе данных.
Так есть ли лучший способ выполнить то, что я хочу?
4 ответа
Хорошо, с тех пор как я задал этот вопрос, я немного поэкспериментировал с этим.
На данный момент, кажется, лучший способ сделать это - вариант 1. Вариант 2 был бы намного лучше, но до тех пор, пока эта функция EFCore не будет реализована, в моем случае это будет невозможно (поддержка существующих баз данных с миграциями на них и поддержка пустых). DBS).
Вариант 1 также имеет несколько подводных камней, на которые я наткнулся (возможно, даже больше, на которые я не наткнулся). Вот как я это сделал:
Создайте новую начальную миграцию:
- Убедитесь, что все ваши миграции были применены к вашей базе данных. Мы создадим новую начальную миграцию, поэтому миграции, которые не были применены, будут потеряны.
- Удалите старые файлы миграции EFCore и файл снимка базы данных.
- Создайте новую начальную миграцию из текущего состояния вашей базы данных. (Например через
dotnet ef migrations add Initial-PostCleanup
.)
Эта новая миграция совместима только с новыми базами данных, так как она создаст все таблицы (и потерпит неудачу, если какие-либо из таблиц, ограничений и т. Д. Уже существуют). Итак, теперь мы собираемся сделать эту миграцию совместимой с существующей базой данных:
- Создайте сценарий SQL для новой начальной миграции через
dotnet ef migrations script -o script.sql
, - Удалить первую транзакцию (до первой
GO
), который создает__EFMigrationsHistory
Таблица:
IF OBJECT_ID(N'[__EFMigrationsHistory]') IS NULL
BEGIN
CREATE TABLE [__EFMigrationsHistory] (
[MigrationId] nvarchar(150) NOT NULL,
[ProductVersion] nvarchar(32) NOT NULL,
CONSTRAINT [PK___EFMigrationsHistory] PRIMARY KEY ([MigrationId])
);
END;
GO
- Удалить последнюю транзакцию, которая вставляет новую запись в
__EFMigrationsHistory
Таблица:
INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
VALUES (N'20190704144924_Initial-PostCleanup', N'2.2.4-servicing-10062');
GO
- Удалить
GO
команды, так как мы поместим скрипт create в оператор IF:
замещатьGO\r\n\r\n
с ничем. - Теперь откройте файл миграции (файл C#, а не файл sql) и замените
Up
метод со следующим:
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql(@"
DECLARE @migrationsCount INT = (SELECT COUNT(*) FROM [dbo].[__EFMigrationsHistory])
IF @migrationsCount = 0
BEGIN
% PASTE YOUR EDITED SQL SCRIPT HERE %
END
");
}
Готово! Все должно работать сейчас!
Обязательно сравните схему базы данных и данные до и после для новой базы данных. Все, что не является частью, если ваша модель EF Code не является частью новой базы данных.
Немного поздно, но у нас была та же проблема в нашем текущем проекте. Выше 400 миграций и 6 метров строк кода внутри. Дизайн. Вот как нам удалось решить эту проблему:
MigrationProject.csproj
<PropertyGroup>
...
<DefaultItemExcludes Condition="'$(Configuration)' == 'Debug' ">$(DefaultItemExcludes);Migrations\**\*.Designer.cs</DefaultItemExcludes>
</PropertyGroup>
Таким образом, вам не нужно сбрасывать миграции в чистое состояние и не удалять файлы.Designer. Вы всегда можете изменить конфигурацию на Release и использовать файлы.Designer любым необходимым способом.
Чтобы сбросить все миграции и обновления с нуля (предположить, что на диске нет полезных данных), могут быть полезны следующие шаги.
(1) Убедитесь, что файл program.cs не оптимизирован для создания / обновления базы данных командой Database.EnsureCreate, так как эта команда предотвращает миграции.
(2) Удалите папку Migrations.
(3) dotnet build
(4) dotnet ef database update 0 -c yourContextFile
(5) dotnet ef migrations add init -c yourContextFile
(6) dotnet ef database update -c yourContextFile
Сегодня я выполнил аналогичную задачу для своего проекта, и я использовал следующие шаги (обновлено из ответа Ниранджана):
- Убедитесь, что файл Program.cs не оптимизирован для создания/обновления базы данных с помощью команды Database.EnsureCreate, поскольку эта команда предотвращает миграцию.
- Резервное копирование целевой производственной базы данных (если что-то пойдет не так, мы сможем восстановить ее с помощью резервных копий).
- Удалите все файлы (включая сценарии миграции и снимки) в папке «Миграции».
- Создайте новую базу данных без каких-либо объектов.
- В локальной среде разработки измените строку подключения к базе данных, чтобы она указывала на вновь созданную пустую базу данных/
- Используйте команду dotnet efmigrations, чтобы создать новый миграционный файл. Например,
dotnet ef migrations add Rebase20230701 --startup-project StartupProjectName --project ProjectNameThatContainsMigrations
- На шаге 5) будет создана новая миграция, например, 20230701012623_Rebase20230701.
- Используйте созданный выше идентификатор миграции, чтобы вручную вставить его в локальную исходную базу данных, например
insert into dbo.__EFMigrationsHistory(MigrationId, ProductVersion) VALUES ('20230701012623_Rebase20230701','7.0.8');
. Это сообщит EF не выполнять никаких реальных миграций. Пожалуйста, сохраняйте записи о предыдущих миграциях в таблице. - Запустите приложение локально, чтобы убедиться, что все работает нормально и данные не потеряны.
- Запустите шаг 7) еще раз в целевой производственной среде.
- Зафиксируйте изменения кода в своем репозитории и разверните приложения в целевой производственной среде. В папке миграции должны быть только файлы 20230701012623_Rebase20230701.cs и файлы моментальных снимков C#.