|
|
@@ -0,0 +1,230 @@
|
|
|
+# Системы миграции данных
|
|
|
+
|
|
|
+Модель данных в процессе разработки имеет свойство изменяться, и в какой-то момент она перестает соответствовать базе данных. Функция **системы миграции** сводится к тому, чтобы синхронизировать структуру БД с моделью данных в приложении без потери существующих данных.
|
|
|
+
|
|
|
+Не помню, затрагивали ли мы этот момент при выборе фреймворка для работы с БД, на всякий случай расскажу еще раз.
|
|
|
+
|
|
|
+Существует два варианта синхронизации модели и базы:
|
|
|
+
|
|
|
+- __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
|
|
|
+ <ItemGroup>
|
|
|
+ ...
|
|
|
+ <EmbeddedResource Include="migrations/*.sql" />
|
|
|
+ </ItemGroup>
|
|
|
+ ```
|
|
|
+
|
|
|
+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 <value>` задает значение по-умолчанию
|
|
|
+
|
|
|
+Рассмотрим 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;
|
|
|
+```
|
|
|
+
|
|
|
+## Задание
|
|
|
+
|
|
|
+Добавить в тестовый проект (Продукты и материалы) систему миграции и какой-нибудь скрипт, меняющий __структуру__ базы данных
|