# Системы миграции данных
Бизнес логика в процессе эксплуатации имеет свойство изменяться, и в какой-то момент модель данных перестает соответствовать базе данных. Функция **системы миграции** сводится к тому, чтобы синхронизировать структуру БД с моделью данных в приложении без потери существующих данных.
Не помню, затрагивали ли мы этот момент при выборе фреймворка для работы с БД, на всякий случай расскажу еще раз.
Существует два варианта синхронизации модели и базы:
- __Code First__ - "сначала код", то есть мы описываем модель и структура базы данных меняется в соответствии с моделью (мы этот подход не используем, он применяется в __Entity Framework__)
- __DB First__ - "база данных первична", то есть мы сначала меняем структуру базы данных и только потом приводим модель в соответствие с ней. Это как раз наш случай.
Для того, чтобы структура базы данных менялась в нужных версиях приложений (раньше в принципе можно, но точно не позже) существуют __системы миграции__, рассмотрим наиболее популярные:
>Взято [отсюда](https://habr.com/ru/companies/crosstech/articles/453930/)
- 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
## Добавление возможности миграции в АПИ
В десктопном приложении использовать миграции можно, но не нужно - нет гарантии, что не запустятся одновременно две копии программы. Правильнее миграции накатывать либо администратору БД (скрипты мы в любом случае будем писать), либо при запуске новой версии АПИ (этот вариант предпочтительнее, так как исключается человеческий фактор - миграция применится в тот момент, когда она нужна)
[Делал по этому туториалу](https://medium.com/@niteshsinghal85/dbup-postgresql-dapper-in-asp-net-core-c3be6c580c54)
1. В проекте с АПИ созадем каталог `migrations`, где будем хранить скрипты для миграции
1. В настройках проекта (файл `*.csproj`) укажем, чтобы все SQL-файлы из этого каталога включались в исполняемый файл
```xml
...
```
1. Создадим файл для миграции `migrations/agent_type.sql`
>Вообще миграции предназначены для изменения структуры БД, но никто не запрещает нам выполнять и DML- скрипты, например, для заполнения словарей
```sql
insert into AgentType values
(1, "индивидуальный предприниматель"),
(2, "Общество с ограниченной ответственностью");
```
1. Устанавливаем библиотеку `DBup`
Можно через __Nuget__, а можно и в консоли:
```sh
dotnet add package dbup-mysql
```
1. Дописываем в __начало__ нашего `Program.cs` код для накатывания миграций
```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 мы еще не работали (базу разворачивали из готового скрипта), напомню какие команды относятся к DDL:
* **CREATE** – используется для создания объектов базы данных;
* **ALTER** – используется для изменения объектов базы данных;
* **DROP** – используется для удаления объектов базы данных.
### Добавление таблиц
В принципе структуру запроса можно и не запоминать - вы всегда можете в _MySQL Workbench_ или _DBeaver_ получить DDL скрипт для существующей таблицы, например `AgentType`:
```sql
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 ` задает значение по-умолчанию
Рассмотрим DDL скрипт таблицы `Agent` - тут у нас появляются внешние ключи:
```sql
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`)``)
### Изменение полей существующей таблицы
#### Добавление поля
```sql
ALTER TABLE table_name
ADD COLUMN new_column_name data_type [constraints];
```
#### Переименовывание поля
* универсальный вариант, меняющий и название поля и его тип
```sql
ALTER TABLE имя_таблицы
CHANGE старое_имя новое_имя ТИП_ДАННЫХ;
```
* простой вариант, меняющий только название поля
```sql
ALTER TABLE имя_таблицы
RENAME COLUMN старое_имя TO новое_имя;
```
#### Удаление поля
```sql
ALTER TABLE table_name
DROP COLUMN column_name;
```
#### Изменение типа данных или ограничений поля
```sql
ALTER TABLE table_name
MODIFY COLUMN column_name new_data_type [new_constraints];
```
#### Добавление внешнего ключа
```sql
ALTER TABLE table_name
ADD CONSTRAINT fk_name
FOREIGN KEY (column_name)
REFERENCES parent_table (parent_column_name);
```
#### Удаление поля
```sql
ALTER TABLE table_name DROP COLUMN column_name;
```
### Удаление таблицы
>Тут надо быть аккуратным, если на эту таблицу есть ссылки из других таблиц, то запрос выдаст ошибку
```sql
DROP TABLE table_name;
```
## Задание
Добавить в тестовый проект (Продукты и материалы) систему миграции и какой-нибудь скрипт, меняющий __структуру__ базы данных