Бизнес логика в процессе эксплуатации имеет свойство изменяться, и в какой-то момент модель данных перестает соответствовать базе данных. Функция системы миграции сводится к тому, чтобы синхронизировать структуру БД с моделью данных в приложении без потери существующих данных.
Не помню, затрагивали ли мы этот момент при выборе фреймворка для работы с БД, на всякий случай расскажу еще раз.
Существует два варианта синхронизации модели и базы:
Для того, чтобы структура базы данных менялась в нужных версиях приложений (раньше в принципе можно, но точно не позже) существуют системы миграции, рассмотрим наиболее популярные:
Взято отсюда
EF Core Migrations
Как видно по названию, является частью Entity Framework. Нативный инструмент, работающий из коробки без каких-либо плясок с бубном. Большое количество документации, официальной и не очень, простота и т.д.
Имеет смысл, если используется Entity Framework.
RoundhousE
Этот инструмент управления миграциями, распространяемый под лицензией Apache 2.0, работает на движке T-SQL миграций (то есть для миграции пишутся DDL-скрипты).
ThinkingHome.Migrator
Инструмент для версионной миграции схемы базы данных под платформу .NET Core, распространяемый под лицензией MIT.
В целом, проект выглядит перспективным, особенно если развивался бы.
Fluent Migrator
Наиболее популярный инструмент миграций, имеющий большую армию поклонников. Распространяется под лицензией Apache 2.0. Как указано в описании, является платформой миграции для .NET, аналогичная Ruby on Rails Migrations. Изменения схемы БД описываются в классах на C#.
Мне он не понравился из-за того, что нужно писать классы для миграций, а это так называемые "проектные знания", которыми не охота вас загружать
DBup
DbUp – это библиотека на .NET, которая помогает накатывать изменения на сервер SQL. Она отслеживает, какие скрипты изменений уже выполнены, и запускает те, которые необходимы для обновления БД. Библиотека выросла из проекта опенсорсного движка блогов на ASP.NET и существует под лицензией MIT, а код лежит на GitHub’е. Миграции описываются с помощью T-SQL.
ИМХО идеальный вариант для наших ПЕТ- проектов: не нужно изучение лишних сущностей, как в Fluent Migrator, достаточно знания SQL
В десктопном приложении использовать миграции можно, но не нужно - нет гарантии, что не запустятся одновременно две копии программы. Правильнее миграции накатывать либо администратору БД (скрипты мы в любом случае будем писать), либо при запуске новой версии АПИ (этот вариант предпочтительнее, так как исключается человеческий фактор - миграция применится в тот момент, когда она нужна)
migrations, где будем хранить скрипты для миграцииВ настройках проекта (файл *.csproj) укажем, чтобы все SQL-файлы из этого каталога включались в исполняемый файл
<ItemGroup>
...
<EmbeddedResource Include="migrations/*.sql" />
</ItemGroup>
Создадим файл для миграции migrations/agent_type.sql
Вообще миграции предназначены для изменения структуры БД, но никто не запрещает нам выполнять и DML- скрипты, например, для заполнения словарей
insert into AgentType values
(1, "индивидуальный предприниматель"),
(2, "Общество с ограниченной ответственностью");
Устанавливаем библиотеку DBup
Можно через Nuget, а можно и в консоли:
dotnet add package dbup-mysql
Дописываем в начало нашего Program.cs код для накатывания миграций
// проверяет наличие возможности подключения к БД
EnsureDatabase.For.MySqlDatabase(connectionString);
// подгатавливает миграцию
// куда и что будем накатывать
var upgrader = DeployChanges.To
.MySqlDatabase(connectionString)
.WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly())
.LogToConsole()
.Build();
// запуск миграции
var result = upgrader.PerformUpgrade();
// проверка результата миграции
if (!result.Successful)
{
throw new Exception("Не смог сделать миграцию");
}
Если все сделали правильно, то в консоли увидите примерно такие логи
2025-09-18 10:31:43 +03:00 [INF] Beginning database upgrade
2025-09-18 10:31:43 +03:00 [INF] Checking whether journal table exists
2025-09-18 10:31:43 +03:00 [INF] Journal table does not exist
2025-09-18 10:31:43 +03:00 [INF] Executing Database Server script 'api_cs.migrations.agent_type.sql'
2025-09-18 10:31:43 +03:00 [INF] Checking whether journal table exists
2025-09-18 10:31:43 +03:00 [INF] Creating the `schemaversions` table
2025-09-18 10:31:44 +03:00 [INF] The `schemaversions` table has been created
2025-09-18 10:31:44 +03:00 [INF] Upgrade successful
А в базе добавится таблица shemaversions, в которой хранится какие миграции и когда применялись (чтобы повторно не накатить уже выполненную миграцию)
Вообще с DDL мы еще не работали (базу разворачивали из готового скрипта), напомню какие команды относятся к DDL:
В принципе структуру запроса можно и не запоминать - вы всегда можете в MySQL Workbench или DBeaver получить DDL скрипт для существующей таблицы, например AgentType:
CREATE TABLE `AgentType` (
`ID` int NOT NULL AUTO_INCREMENT,
`Title` varchar(50) NOT NULL,
`Image` varchar(100) DEFAULT NULL,
PRIMARY KEY (`ID`)
)
Думаю тут все более менее очевидно - задается название таблицы и описываются ее поля
NOT NULL означает, что поле обязательноеDEFAULT <value> задает значение по-умолчаниюРассмотрим DDL скрипт таблицы Agent - тут у нас появляются внешние ключи:
CREATE TABLE `Agent` (
`ID` int NOT NULL AUTO_INCREMENT,
`Title` varchar(150) NOT NULL,
`AgentTypeID` int NOT NULL,
`Address` varchar(300) DEFAULT NULL,
`INN` varchar(12) NOT NULL,
`KPP` varchar(9) DEFAULT NULL,
`DirectorName` varchar(100) DEFAULT NULL,
`Phone` varchar(20) NOT NULL,
`Email` varchar(255) DEFAULT NULL,
`Logo` varchar(100) DEFAULT NULL,
`Priority` int NOT NULL,
PRIMARY KEY (`ID`),
CONSTRAINT `FK_Agent_AgentType` FOREIGN KEY (`AgentTypeID`) REFERENCES `AgentType` (`ID`)
);
AgentTypeID) создается как обычноCONSTRAINT), в котором мы описываем, что поле является внешним ключем (FOREIGN KEY (`AgentTypeID`)) и ссылается на таблицу и поле в ней (REFERENCES `AgentType` (`ID`))ALTER TABLE table_name
ADD COLUMN new_column_name data_type [constraints];
универсальный вариант, меняющий и название поля и его тип
ALTER TABLE имя_таблицы
CHANGE старое_имя новое_имя ТИП_ДАННЫХ;
простой вариант, меняющий только название поля
ALTER TABLE имя_таблицы
RENAME COLUMN старое_имя TO новое_имя;
ALTER TABLE table_name
DROP COLUMN column_name;
ALTER TABLE table_name
MODIFY COLUMN column_name new_data_type [new_constraints];
ALTER TABLE table_name
ADD CONSTRAINT fk_name
FOREIGN KEY (column_name)
REFERENCES parent_table (parent_column_name);
ALTER TABLE table_name DROP COLUMN column_name;
Тут надо быть аккуратным, если на эту таблицу есть ссылки из других таблиц, то запрос выдаст ошибку
DROP TABLE table_name;
Добавить в тестовый проект (Продукты и материалы) систему миграции и какой-нибудь скрипт, меняющий структуру базы данных