|
|
3 місяців тому | |
|---|---|---|
| .. | ||
| Screen | 3 місяців тому | |
| readme.md | 3 місяців тому | |
Fluent Migrator - это фреймворк с открытым исходным кодом для .NET, предназначенный для управления миграциями базы данных. В отличие от подхода "код первичен" (Code-First) в Entity Framework, где миграции генерируются автоматически, Fluent Migrator предоставляет разработчику полный контроль над процессом, позволяя описывать изменения схемы базы данных на языке C# с помощью удобного Fluent API.
Основная философия Fluent Migrator заключается в принципе "миграции как код". Это означает, что каждая версия схемы вашей базы данных представлена в виде класса C#, который можно версионировать с помощью системы контроля версий (такой как Git) вместе с кодом приложения. Это обеспечивает последовательность, повторяемость и надежность процесса развертывания базы данных в разных средах (разработка, тестирование, продакшн).
Одним из ключевых преимуществ Fluent Migrator является его способность абстрагироваться от конкретной системы управления базами данных. Вы пишете миграции один раз, а фреймворк генерирует соответствующий SQL код для различных СУБД - SQL Server, PostgreSQL, MySQL, SQLite и других.
Ключевые особенности:
Миграции можно накатывать как из приложения, так и внешними командами. В зависимости от этого нужно устанавливать разные пакеты:
Пакет FluentMigrator - нужен для создания классов миграций, то есть ставим обязательно
dotnet add package FluentMigrator
Остальные пакеты добавляем только если предполагается установка миграций из приложения
# Runner для применения миграций
dotnet add package FluentMigrator.Runner
# Поддержка SQL Server
dotnet add package FluentMigrator.Runner.SqlServer
# Поддержка PostgreSQL
dotnet add package FluentMigrator.Runner.Postgres
# Поддержка MySQL
dotnet add package FluentMigrator.Runner.MySql
Каждая миграция в Fluent Migrator представляет собой класс, помеченный атрибутом Migration с указанием уникального номера версии. Номер версии обычно представляет собой timestamp в формате YYYYMMDDNN, что гарантирует хронологический порядок применения миграций.
Миграция содержит два основных метода: Up() и Down(). Метод Up() описывает изменения, которые должны быть применены к базе данных, в то время как метод Down() определяет, как эти изменения можно откатить. Этот подход обеспечивает возможность отката к предыдущему состоянию схемы, что критически важно для безопасного развертывания.
[Migration(20251016001)]
public class CreateUserTable : Migration
{
public override void Up()
{
// Код для применения миграции
}
public override void Down()
{
// Код для отката миграции
}
}
Fluent Migrator использует шаблон Fluent Interface, который позволяет создавать читаемый и выразительный код. Вместо того чтобы писать сложные SQL-запросы, вы используете цепочки методов, которые интуитивно понятны даже разработчикам, не являющимся экспертами в SQL.
(Например, создание таблицы выглядит как последовательность вызовов методов: Create.Table(), затем WithColumn(), и далее спецификация каждого столбца с его типом и ограничениями. Такой подход не только улучшает читаемость, но и обеспечивает проверку типов на этапе компиляции.)
Структурные миграции отвечают за изменения схемы базы данных. Это включает:
Эти миграции являются наиболее распространенными и формируют основу эволюции схемы базы данных.
Миграции данных управляют содержимым таблиц. Они используются для:
Важной особенностью миграций данных является то, что они могут содержать сложную бизнес-логику, реализованную на C#, что предоставляет гораздо больше возможностей по сравнению с чистыми SQL-скриптами.
Для сложных сценариев Fluent Migrator предоставляет возможность выполнения произвольного SQL кода, создания хранимых процедур, функций и триггеров. Это позволяет охватить все аспекты управления базами данных, даже те, которые не поддерживаются напрямую Fluent Interface.
Execute.Sql("INSERT INTO MyTable (Column1, Column2) VALUES ('Value1', 'Value2');");
Execute.Sql("UPDATE AnotherTable SET Status = 'Active' WHERE Id = 1;");
Поскольку миграции пишутся на C#, компилятор может обнаружить многие ошибки на этапе компиляции, что невозможно при использовании SQL-скриптов. Это значительно снижает вероятность ошибок в продуктивной среде.
Миграции хранятся как файлы кода, что позволяет легко отслеживать историю изменений схемы базы данных, видеть, кто и когда внес изменение, и возвращаться к предыдущим версиям.
Процесс применения миграций может быть легко встроен в конвейеры непрерывной интеграции и развертывания (CI/CD). Это гарантирует, что все среды имеют идентичную схему базы данных.
Fluent Migrator поддерживает широкий спектр баз данных, включая SQL Server, PostgreSQL, MySQL, SQLite, Oracle и другие. Абстракция Fluent API позволяет писать миграции, не зависящие от конкретной СУБД, хотя при необходимости можно использовать и специфические функции.
Оба инструмента решают одну задачу — управление миграциями базы данных, но подходят к этому с разной философией. Вот детальное сравнение.
| Критерий | Fluent Migrator | DbUp |
|---|---|---|
| Подход | "Код первичен" | "База первична" |
| SQL-генерация | Автоматическая | Ручное написание |
| Кросс-платформенность | Один код для разных СУБД | Требуются разные SQL для разных СУБД |
| Откат изменений | Встроенная поддержка | Ручное написание скриптов отката |
| Гибкость | Ограничена API | Полная (любой SQL) |
| Типичное использование | Проекты с частым изменением структуры БД | Сложные миграции |
| Интеграция с БД | через абстракции | Прямая работа с SQL |
Выбирайте Fluent Migrator если:
Выбирайте DbUp если:
Fluent Migrator следует принципу "миграции как код" (модель базы данных определяется исключительно последовательностью миграций), в то время как EF Core использует подход "код первичен" (модель базы данных выводится из классов-сущностей).
В Fluent Migrator вы пишете каждую миграцию вручную, имея полный контроль. В EF Core миграции генерируются автоматически на основе изменений в ваших моделях, хотя их также можно настраивать вручную.
Для очень больших проектов с сложной схемой ручное управление миграциями в Fluent Migrator может быть более производительным и предсказуемым, чем автоматическое сравнение моделей в EF Core.
Разработчик определяет, какие изменения должны быть внесены в схему базы данных, и реализует соответствующие операции в методе Up(). Одновременно необходимо продумать, как эти изменения можно будет откатить, и реализовать метод Down().
Вывести в консоли должно типо такого:
В MySQL:
Важно соблюдать принцип атомарности миграций - каждая миграция должна представлять собой логически завершенное изменение. Это упрощает откат и делает процесс более предсказуемым.
Fluent Migrator поддерживает несколько способов применения миграций:
В процессе применения Fluent Migrator отслеживает, какие миграции уже были применены, и выполняет только те, которые еще не были выполнены на целевой базе данных.
Одной из ключевых возможностей Fluent Migrator является ведение таблицы версий, которая хранит информацию о примененных миграциях. Это позволяет системе точно знать текущее состояние схемы базы данных и определять, какие миграции необходимо применить для перехода к желаемой версии.
DDL (Data Definition Language) - это подмножество SQL, отвечающее за определение и изменение структуры базы данных:
CREATE - создание обьектов (таблиц, индексов, etc.)ALTER - изменение существующих объектовDROP - Удаление объектоTRUNCATE - очистка таблицRENAME - переименовние оюъектовПоддержка различных СУБД — библиотека позволяет использовать один фреймворк миграций в разных проектах и средах.
Отслеживание изменений — изменения в базе данных назначаются номером версии, что позволяет безопасно откатывать изменения при необходимости.
Автоматизация процесса — инструмент можно интегрировать в инструменты CI (Continuous integration) для автоматизации миграций.
Зависимость от СУБД - функциональность библиотеки сильно зависит от поставщиков баз данных, которые могут иметь свои особенности и ограничения.
Влияние на производительность - в зависимости от сложности миграций инструмент может оказывать заметное влияние на производительность приложения.
public static void Main(string[] args)
{
var serviceProvider = CreateServices();
using (var scope = serviceProvider.CreateScope())
{
UpdateDatabase(scope.ServiceProvider);
}
}
private static IServiceProvider CreateServices()
{
return new ServiceCollection()
.AddFluentMigratorCore()
.ConfigureRunner(rb => rb
.AddMySql5() // выбор базы данных из строки подключения
.WithGlobalConnectionString(
"Server=kolei.ru; Database=dkoryakova_test; Uid=dkoryakova; Pwd=061007;") // подключение к бд
// Указание сборки, в которой находятся миграции
.ScanIn(typeof(Program).Assembly)
.For
.Migrations())
.AddLogging(lg => lg.AddFluentMigratorConsole()) // настройка логирования
.BuildServiceProvider(false);
}
public static void UpdateDatabase(
IServiceProvider serviceProvider)
{
var runner = serviceProvider.GetRequiredService<IMigrationRunner>(); // построение поставщика услуг
runner.MigrateUp(); //запуск миграции
}
public override void Up()
{
// Пользователи
Create.Table("Users")
.WithColumn("Id")
.AsInt32()
.PrimaryKey()
.Identity() //Identity() гарантирует, что все значения будут уникальными
.WithColumn("UserName")
.AsString(50)
.NotNullable()
.Unique() //Unique() гарантирует, что значения в определенном столбце всегда будут неповторяющимися
.WithColumn("Email")
.AsString(100)
.NotNullable()
.Unique() //NotNullable() гарантирует что обязательные поля всегда будут заполнены
.WithColumn("PasswordHash")
.AsString(255)
.NotNullable()
.WithColumn("CreatedAt")
.AsDateTime()
.NotNullable()
.WithDefaultValue(SystemMethods.CurrentDateTime)
.WithColumn("IsActive")
.AsBoolean()
.NotNullable()
.WithDefaultValue(true);
// Роли
Create.Table("Roles")
.WithColumn("Id")
.AsInt32()
.PrimaryKey()
.Identity()
.WithColumn("Name")
.AsString(50)
.NotNullable()
.Unique()
.WithColumn("Description")
.AsString(255)
.Nullable();
// Связь пользователей и ролей
Create.Table("UserRoles")
.WithColumn("UserId")
.AsInt32()
.NotNullable()
.WithColumn("RoleId")
.AsInt32()
.NotNullable();
// Внешние ключи
Create.PrimaryKey("PK_UserRoles_Users")
.OnTable("UserRoles")
.Columns("UserId", "RoleId");
Create.ForeignKey("FK_UserRoles_Users")
.FromTable("UserRoles")
.ForeignColumn("UserId")
.ToTable("Users")
.PrimaryColumn("Id")
.OnDelete(Rule.Cascade);
Create.ForeignKey("FK_UserRoles_Roles")
.FromTable("UserRoles")
.ForeignColumn("RoleId")
.ToTable("Roles")
.PrimaryColumn("Id")
.OnDelete(Rule.Cascade);
}
public override void Down()
{
// Удаляем в обрадном порядке создания
Delete.Table("UserRoles");
Delete.Table("Roles");
Delete.Table("Users");
}
public override void Up()
{
// Изменение типа данных
Alter.Table("Users")
.AlterColumn("UserName")
.AsString(100)
.NotNullable();
}
public override void Up()
{
// Добавление данных
Insert.IntoTable("Roles")
.Row(new {
Name = "Administrator",
Description = "System administrator" })
.Row(new {
Name = "User",
Description = "Regular user" });
}
public override void Down()
{
Delete
.ForeignKey("FK_UserRoles_Roles")
.OnTable("UserRoles");
Delete
.ForeignKey("FK_UserRoles_Users")
.OnTable("UserRoles");
Delete
.PrimaryKey("PK_UserRoles_Users")
.FromTable("UserRoles");
}
Глобально устранавливаем консольную утилиту для применения/отката миграций
dotnet tool install -g FluentMigrator.DotNet.Cli
Не забываем собрать приложение, чтобы в скомпилированном файле были последние классы миграций
dotnet build
dotnet-fm migrate \
-p Mysql \
-c "Server=kolei.ru; Database=dkoryakova_new; Uid=dkoryakova; Pwd=061007;" \
-a "путь к api.dll" \
--verbose
dotnet-fm - консольная утилита-p - выбераем тип базы данных-c - задаем строку подключения к серверу-а - путь к приложению, в котором описаны миграции--verbose - для получения подробных логов при миграцииdotnet-fm rollback \
-p MySql \
-c "Server=kolei.ru; Database=dkoryakova_new; Uid=dkoryakova; Pwd=061007;" \
-a "путь к api.dll"