После разработки нашего приложения некоторое время мы накопили довольно много миграций базы данных EFCore. Поскольку EFCore добавляет моментальный снимок всей модели базы данных при каждой миграции, этот код добавляет довольно много. После анализа около 80% нашего времени компиляции уходит на миграции (компиляция + анализаторы Roslyn).
Итак, пришло время очистить некоторые старые миграции! Но как лучше всего это сделать? Официальных указаний по этому поводу вроде нет...
Нам не нужны никакие откаты (мы только откатываемся вперед), так что все упрощается. Нам нужна поддержка создания базы данных с нуля и обновления базы данных из последних нескольких миграций.
Что я пробовал:
Ядерный вариант, по-видимому, заключается в удалении всех миграций и моментального снимка модели и создании новой начальной миграции. Хотя это нормально, это кажется немного опасным. При таком подходе нам нужно быть очень осторожными, чтобы каждая часть схемы базы данных была частью модели кода. Один пограничный случай, с которым мы, например, столкнулись, заключается в том, что EFCore еще не поддерживает проверенные ограничения. Поэтому мы добавили проверенное ограничение в миграцию, но не в модель кода. Таким образом, при создании новой начальной миграции проверенное ограничение не было ее частью.
В качестве эксперимента я попытался удалить снимок модели из всех старых миграций, так как снимки составляют 90% кода, что вызывает длительное время компиляции. Я понял, что EFCore использует моментальный снимок только в качестве инструмента сравнения для новой миграции. Однако после удаления моментального снимка старые миграции больше не выполнялись, когда они запускались в новой базе данных.
Так есть ли лучший способ выполнить то, что я хочу?
Только что проверил, работает ли решение 2 на EFCore 2.0. Столкнулся с той же проблемой, которую я описал (свежая БД не выполняет миграцию без файла Designer).
Я создал задачу в репозитории EFCore для решения 2: github.com/aspnet/EntityFrameworkCore/issues/16331
Первое решение работает для меня, но да, вы упомянули хороший момент, когда это может быть опасно.





Хорошо, с тех пор как я задал этот вопрос, я немного поэкспериментировал с этим.
На данный момент кажется, что лучший способ сделать это — вариант 1. Вариант 2 был бы намного лучше, но пока не будет реализован эта функция EFCore, это не совсем выполнимо для моего варианта использования (поддержка существующих баз данных с миграциями на них и поддержка пустых баз данных) .
Вариант 1 также имеет несколько подводных камней, на которые я наткнулся (может быть, даже больше, на которые я не наткнулся). Итак, вот как я это сделал:
Создайте новую начальную миграцию:
dotnet ef migrations add Initial-PostCleanup.)Эта новая миграция совместима только с новыми базами данных, поскольку она создаст все таблицы (и произойдет сбой, если какая-либо из таблиц, ограничений и т. д. уже существует). Итак, теперь мы собираемся сделать эту миграцию совместимой с существующей базой данных:
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, так как мы поместим скрипт создания в оператор IF:GO\r\n\r\n ничем.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 не является частью новой базы данных.
Работает отлично, обязательно повторно добавьте пользовательские скрипты из старых миграций. Например, мне пришлось добавить код для создания представления, которое в противном случае не существовало бы при создании новой базы данных.
Возможно, при использовании этого решения есть одна ловушка: вы потеряете независимость от базы данных, поскольку скопированный SQL теперь специфичен для вашего типа базы данных.
Любая причина, по которой нельзя было бы просто а.) удалить миграции б.) создать начальную и в.) удалить все, кроме первого элемента в __migrationhistory в таблице базы данных, и обновить первую запись, чтобы она имела правильное имя и хэш, как указано в снимок?
@Barry Вы имеете в виду делать только эти 3 вещи вместо всех вещей, которые я описал в ответе? Или вы имеете в виду что-то более конкретное?
Да точно, просто интересно. Поскольку это работает для меня, я не был уверен, что это фатально оставляет что-то за кадром; такие как индексы, ограничения и т.д.? Заранее спасибо! очень ценю ответ
Ну, не тестировал, и это было некоторое время. Но главная проблема заключалась в том, чтобы убедиться, что новая миграция не пытается воссоздать таблицы, если они уже существуют. Я предполагаю, что вы выполняете шаг c вручную в БД, а не через миграцию, верно? Если это нормально для вас, то ваше решение кажется идеальным. У меня было ограничение, что я не мог (и не хотел) изменять производственную базу данных вручную.
Немного поздно, но у нас была такая же проблема в нашем текущем проекте. Более 400 миграций и 6 миллионов строк кода внутри .Designer. Вот как нам удалось решить эту проблему:
MigrationProject.csproj
<PropertyGroup>
...
<DefaultItemExcludes Condition = "'$(Configuration)' == 'Debug' ">$(DefaultItemExcludes);Migrations\**\*.Designer.cs</DefaultItemExcludes>
</PropertyGroup>
Таким образом, вам не нужно ни сбрасывать миграцию в чистое состояние, ни удалять файлы .Designer. Вы всегда можете изменить конфигурацию на Release и использовать файлы .Designer любыми средствами.
Да, это то, что нужно дать вам некоторое время, реальное решение проблемы — это «перезагрузка», которую мы просто не можем пропустить.
Если вы хотите сохранить только последние файлы конструктора миграции, см. мой ответ: stackoverflow.com/a/65004969/331281
Чтобы сбросить все миграции и обновления с нуля (предположим, что на диске нет полезных данных), могут быть полезны следующие шаги.
(1) Убедитесь, что файл program.cs не оптимизирован для создания/обновления базы данных с помощью команды Database.EnsureCreate, так как эта команда предотвращает миграцию.
(2) Удалить папку Миграции.
(3) сборка дотнета
(4) обновление базы данных dotnet ef 0 -c yourContextFile
(5) миграция dotnet ef добавляет init -c yourContextFile
(6) обновление базы данных dotnet ef -c yourContextFile
Странно, согласно это, решение 2 должно было сработать.