簡介
概念定位
FluentMigrator是一個基於“流式 API”(Fluent API)的.NET數據庫版本遷移框架。- 核心目標:以代碼形式對數據庫結構(
Schema)進行增量變更管理,支持SQL Server、PostgreSQL、MySQL、SQLite、Oracle等多種數據庫。 -
核心優勢:
- 可讀性高:以鏈式方法描述表、列、索引等,而非手寫
SQL; - 可編程:可藉助
C#邏輯(條件、循環)來生成遷移; - 可版本化:每個遷移都有唯一版本號,保證在不同環境中有序執行;
- 可回滾:內置
Down方法,支持自動回退。
- 可讀性高:以鏈式方法描述表、列、索引等,而非手寫
核心價值
- 傳統 SQL 腳本管理的痛點
| 問題 | 影響 | FluentMigrator 解決方案 |
|---|---|---|
| 腳本執行順序混亂 | 數據庫狀態不一致 | 版本號強制順序執行 |
| 環境差異處理困難 | 開發/測試/生產不一致 | 代碼統一管理所有環境 |
| 回滾機制缺失 | 錯誤修復困難 | 支持 Up/Down 雙向遷移 |
| 狀態追蹤困難 | 不知當前數據庫版本 | 內置版本追蹤表 |
| 團隊協作衝突 | 合併衝突頻發 | 代碼合併更簡單 |
- 核心優勢
安裝與配置
NuGet引入
在項目(建議單獨建一個 Migrations 項目)中安裝:
dotnet add package FluentMigrator
dotnet add package FluentMigrator.Runner
dotnet add package FluentMigrator.Runner.SqlServer
dotnet add package FluentMigrator.Runner.MySql
注意先得有 ADO 驅動包,例如:MySql.Data
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentMigrator" Version="7.1.0" />
<PackageReference Include="FluentMigrator.Runner" Version="7.1.0" />
<PackageReference Include="FluentMigrator.Runner.MySql" Version="7.1.0" />
<PackageReference Include="MySql.Data" Version="9.4.0" />
</ItemGroup>
</Project>
若要在 .NET Core CLI 中使用,可額外安裝:
dotnet tool install --global FluentMigrator.DotNet.Cli
- 配置
Runner
在 .NET Core 控制枱或 ASP.NET Core 的 Program.cs 中:
using FluentMigrator.Runner;
var serviceProvider = new ServiceCollection()
.AddFluentMigratorCore()
.ConfigureRunner(rb => rb
.AddSqlServer() // 指定數據庫類型
.WithGlobalConnectionString(Configuration.GetConnectionString("Default"))
.ScanIn(typeof(Program).Assembly).For.Migrations()
)
.AddLogging(lb => lb.AddFluentMigratorConsole()) // 控制枱日誌
.BuildServiceProvider();
// 執行遷移
using var scope = serviceProvider.CreateScope();
var runner = scope.ServiceProvider.GetRequiredService<IMigrationRunner>();
runner.MigrateUp(); // 或 runner.MigrateDown(targetVersion);
核心概念
-
Migration(遷移類)- 每個遷移類繼承自
Migration,通過Version屬性或構造參數指定版本號(通常為yyyyMMddHHmm形式)。 - 重寫
Up()和Down()方法,分別描述版本升級和降級的操作。
- 每個遷移類繼承自
-
Runner(運行器)IMigrationRunner提供執行、回滾、版本查詢等功能。- 通過依賴注入或
CLI工具觸發。
-
版本表
- 默認在數據庫中維護一張
VersionInfo表,用於存儲已執行的遷移版本號集合。
- 默認在數據庫中維護一張
創建與執行遷移
編寫遷移
[Migration(202507150901)]
public class CreateUsersTable : Migration
{
public override void Up()
{
Create.Table("Users")
.WithColumn("Id").AsInt32().PrimaryKey().Identity()
.WithColumn("Username").AsString(100).NotNullable()
.WithColumn("Email").AsString(200).Nullable()
.WithColumn("CreatedAt").AsDateTime().WithDefault(SystemMethods.CurrentUTCDateTime);
}
public override void Down()
{
Delete.Table("Users");
}
}
Create.Table()、.WithColumn()、.AsXxx():流式構造表結構。SystemMethods.CurrentUTCDateTime:框架內置常用SQL函數。
執行遷移(CLI)
常用命令參數
| 參數 | 説明 | 示例 |
|---|---|---|
-c |
連接字符串 | -c "Server=.;Database=Test;..." |
-p |
數據庫提供程序 | -p sqlserver2016 |
-a |
遷移程序集 | -a "Migrations.dll" |
-t |
版本號 | -t 202306010002 |
-o |
輸出 SQL 到文件 | -o migrations.sql |
--preview |
預覽模式 | --preview |
遷移文件詳解
基本遷移結構
[Migration(202305010001)] // 唯一版本號
public class InitialSchema : Migration
{
public override void Up()
{
// 升級操作
Create.Table("Users")
.WithColumn("Id").AsInt32().PrimaryKey().Identity()
.WithColumn("Name").AsString(100).NotNullable()
.WithColumn("CreatedAt").AsDateTime().NotNullable().WithDefault(SystemMethods.CurrentDateTime);
}
public override void Down()
{
// 回滾操作
Delete.Table("Users");
}
}
常用操作示例
創建表
Create.Table("Products")
.WithColumn("Id").AsInt32().PrimaryKey().Identity()
.WithColumn("Name").AsString(100).NotNullable()
.WithColumn("Price").AsDecimal(10, 2).NotNullable()
.WithColumn("CategoryId").AsInt32().ForeignKey("Categories", "Id");
修改表結構
Alter.Table("Users")
.AddColumn("LastLogin").AsDateTime().Nullable()
.AlterColumn("Name").AsString(150).NotNullable();
創建索引
Create.Index("IX_Users_Email")
.OnTable("Users")
.OnColumn("Email").Ascending()
.WithOptions().Unique()
.WithOptions().NonClustered();
數據遷移
Insert.IntoTable("AuditLog")
.Row(new { Action = "PriceUpdate", UserId = 1, Timestamp = DateTime.UtcNow });
執行自定義 SQL
[Migration(2025071505)]
public class CustomSqlMigration : Migration
{
public override void Up()
{
Execute.Sql("UPDATE Users SET Age = Age + 1 WHERE Name LIKE 'J%'");
}
public override void Down()
{
Execute.Sql("UPDATE Users SET Age = Age - 1 WHERE Name LIKE 'J%'");
}
}
高級特性
事務控制
[Migration(202306010003, TransactionBehavior.None)] // 禁用事務
public class LargeDataMigration : Migration
{
public override void Up()
{
// 大數據遷移操作(分批次執行)
Execute.WithConnection((conn, trans) =>
{
// 手動事務控制
using var cmd = conn.CreateCommand();
cmd.CommandText = "INSERT ...";
cmd.ExecuteNonQuery();
});
}
}
環境特定遷移
[Migration(202306150004)]
[Tags("Development", "Staging")] // 只應用於特定環境
public class AddTestData : Migration
{
public override void Up()
{
if (ApplicationEnvironment.Current == "Development")
{
Insert.IntoTable("Users")
.Row(new { Name = "TestUser", Email = "test@example.com" });
}
}
}
條件遷移
- 使用
IfDatabase()適配不同數據庫:
IfDatabase("SqlServer").Create.Table("Users").WithColumn("Id").AsInt32();
集成 CI/CD
- 在啓動時運行遷移,或使用
dotnet fm工具
dotnet fm migrate -p sqlite -c "Data Source=test.db" -a Migrations.dll
自定義遷移基類
public abstract class AuditMigration : Migration
{
protected void AddAuditColumns(string tableName)
{
Alter.Table(tableName)
.AddColumn("CreatedBy").AsString(50).NotNullable().WithDefaultValue("system")
.AddColumn("CreatedAt").AsDateTime().NotNullable().WithDefault(SystemMethods.CurrentDateTime)
.AddColumn("UpdatedBy").AsString(50).Nullable()
.AddColumn("UpdatedAt").AsDateTime().Nullable();
}
}
[Migration(202307010005)]
public class AddAuditToProducts : AuditMigration
{
public override void Up() => AddAuditColumns("Products");
}
數據庫特定語法
public override void Up()
{
IfDatabase("sqlserver").Create.Column("RowVersion").OnTable("Users").AsCustom("ROWVERSION");
IfDatabase("postgres").Create.Column("RowVersion").OnTable("Users").AsInt64().WithDefaultValue(0);
}
回滾與版本控制
-
回滾策略
runner.MigrateDown(long version):回退到指定版本,所有高於該版本的 Up 操作都會執行其 Down。runner.Rollback(int steps):回退指定步數。
-
版本表管理
- 遷移執行後,
Runner會在VersionInfo表中插入記錄。 - 手動清理、或在測試數據庫初始化時,可先
runner.ListMigrations()、再runner.MigrateDown(0)全部回滾。
- 遷移執行後,
進階特性
標籤(Tags)
- 可給遷移打標籤,按環境有選擇地執行:
[Migration(202507150901, TransactionBehavior.Default, "dev","test")]
public class DevOnlyMigration : Migration { … }
Runner執行時傳入Tags = new[] { "test" },只運行標記匹配的遷移。
事務控制
- 默認每個
Up()/Down()在單個事務中執行,可通過TransactionBehavior.None禁用事務包裹。
自定義腳本與模板
- 在
Up()中可調用Execute.Sql("…"),執行任意SQL。 - 支持加載嵌入資源腳本:
Execute.EmbeddedScript("Namespace.Scripts.SeedData.sql");
多數據庫分支
- 通過條件判斷在
Up()中針對不同RunnerContext.Provider執行不同邏輯,實現一個遷移類多庫支持。
Preview 與 Dry‑Run
- 使用日誌器攔截生成的
SQL,而不真正執行,適合代碼review或審計。
與 Entity Framework 遷移對比
| 特性 | FluentMigrator | EF Core Migrations |
|---|---|---|
| 數據庫支持 | 廣泛(20+種) | 主流數據庫 |
| ORM 依賴 | 無 | 依賴 EF Core |
| 遷移控制 | 精確控制 | 自動生成 |
| 複雜操作 | 支持原生 SQL | 限制較多 |
| 版本管理 | 顯式版本號 | 自動排序 |
| 回滾能力 | 完整 Down 腳本 | 部分支持 |
| 適用場景 | 複雜企業系統 | EF Core 項目 |
最佳實踐
-
版本號規範
- 建議使用
UTC時間戳(如202507150901),保證版本唯一且可排序。
- 建議使用
-
小步提交
- 每個遷移專注一個功能點,避免合併衝突。
-
編寫
Down- 同步維護
Down()方法,以便緊急回退;如果不支持,可在類上標記跳過回滾。
- 同步維護
-
環境隔離
- 在測試/本地環境使用內存或專用數據庫,避免干擾生產。
-
審計與審查
- 將遷移代碼納入代碼審查流程,必要時開啓
Dry‑Run,打印SQL預覽。
- 將遷移代碼納入代碼審查流程,必要時開啓
-
腳本化種子數據
- 如需初始化必備字典表,可在遷移中引入嵌入式
SQL腳本而非硬編碼。
- 如需初始化必備字典表,可在遷移中引入嵌入式
資源與擴展
- GitHub:https://github.com/fluentmigrator/fluentmigrator
- 文檔:https://fluentmigrator.github.io
-
NuGet 包:
FluentMigrator:核心遷移框架。FluentMigrator.Runner:運行器。FluentMigrator.Runner.SqlServer:SQL Server 支持。
-
擴展工具:
Alt.FluentMigrator.VStudio:Visual Studio 集成,簡化遷移生成。dotnet-fm:命令行工具,執行遷移。