一種更安全、可控的 EF Core 自動遷移方案:模塊化架構下的工程化實踐
在現代應用開發中,數據庫遷移已經成為不可或缺的工程環節。
特別是在 模塊化架構、分佈式系統、多團隊協作 的背景下,傳統 EF Core 遷移雖然足夠靈活,但在工程化落地方面仍存在一些現實挑戰,例如:
- 各模塊擁有獨立的 DbContext
- 多人並行開發導致遷移腳本衝突
- 生產環境通常禁止破壞性更改
- 某些實體(例如 DTO)不應出現在數據庫中
- 不同數據庫對大小寫的敏感性差異會引發遷移問題
為了在保持 EF Core 使用體驗的基礎上增強其工程化能力,我設計並開源了一個遷移增強庫 —— Daibitx.EFCore.AutoMigrate。
它的目標並不是替代 EF Core,而是在其基礎之上提供一種更穩健、安全且更適合模塊化工程的遷移方式。
本文將從工程需求出發,系統介紹這一方案背後的設計理念、解決的核心問題,以及它在模塊化項目中的實際表現。
📘 GitHub(開源地址)
https://github.com/daibitx/Daibitx.EFCore.AutoMigrate 歡迎 Star,這是對開源作者最大的支持。
📦 NuGet 包
https://www.nuget.org/packages/Daibitx.EFCore.AutoMigrate
一、為什麼傳統 EF Core 遷移在工程化場景不夠用?
雖然 EF Core 的 Migrations 機制功能豐富,但它默認假設的場景是:
單應用、單團隊、單模型、單數據庫。
而當架構逐漸模塊化或開始多人協作時,一些問題會不可避免地出現。
1. 多 DbContext / 多模塊帶來的結構管理難度
每個模塊維護自己的實體、自己的上下文,一旦參與同一個數據庫結構,就會變成“多方共建”模式,而 EF Core 並沒有提供模塊級的安全隔離。
2. 多團隊並行開發容易產生遷移衝突
例如:
- A 模塊添加字段
- B 模塊刪除字段
如果缺少安全邊界,生產庫可能遭遇不可逆的破壞。
3. 生產環境通常不允許高風險操作
包括但不限於:
- Drop Table
- Drop Column
- Rename Column
- 修改列類型
這些操作在生產環境中極其危險,哪怕在開發環境看似正常。
4. 某些類是 DTO/ReadModel,不應進入遷移系統
真實項目裏有大量 DTO 類,例如:
UserDtoOrderQueryDtoProductItemDTO
這些類只是:
- 視圖模型
- 讀模型
- API 輸入輸出結構
- 查詢模型(CQRS)
如果誤被 EF Core 管理,將導致數據庫生成無意義的表。
5. 多數據庫的大小寫敏感差異會導致失敗
PostgreSQL、MySQL 對大小寫敏感,而 SQL Server 不敏感。
這會導致遷移過程出現:
- 表名衝突
- 索引重複
- 列名衝突
這些通常在生產才暴露,非常危險。
二、Daibitx.EFCore.AutoMigrate 的設計目標
針對以上問題,本方案的目標十分明確:
在 EF Core 原生能力之上,構建一套更安全、更可控、更適合模塊化的“增強遷移層”。
核心原則包括:
- 默認安全,不允許破壞性操作
- 遷移過程具備事務性
- 支持 DTO/只讀模型過濾
- 兼容多數據庫環境
- 併發遷移安全
- 設計時服務可模塊化加載
換句話説:
它不是為了“更智能”,而是為了“更可控”。
這也是生產系統真正需要的。
三、關鍵能力解析
1. 安全模式(SafeMode)
SafeMode 是生產環境最核心的能力。
它會禁止:
- Drop Table
- Drop Column
- Modify Column
- Rename Table / Rename Column
只允許:
- Add Table
- Add Column
- Add Index
- Add ForeignKey
避免遷移腳本對其他團隊造成破壞,是模塊化架構的基礎。
2. 事務式遷移執行
遷移步驟全部在數據庫事務中執行:
- 任一步驟失敗則自動回滾
- 保證遷移結果一致性
- 避免出現“遷移中斷導致數據庫半結構”的問題
這在生產環境尤其關鍵。
3. DTO 自動過濾(工程化特性,強烈推薦)
這是基於真實工程場景加入的最實用功能之一:
任何以
Dto(大小寫不敏感)結尾的實體,將自動排除在遷移之外。
源代碼過濾邏輯如下:
.Where(e => !e.ClrType.Name.EndsWith("Dto", StringComparison.OrdinalIgnoreCase))
4. 併發安全
服務端集羣啓動時,多個實例同時執行遷移極容易導致:
- 死鎖
- 部分實例失敗
AutoMigrate 內置併發安全保護,確保遷移只執行一次。
5. 多數據庫 Provider 支持
包括:
- SQL Server
- MySQL
- PostgreSQL
- SQLite
- 以及其他支持 EF Core 的 Provider
四、使用示例
方式 1:在 ASP.NET Core 中自動遷移(推薦)
app.Services.AutoMigrate<MyDbContext>(services =>
{
// 配置設計時服務
}, options =>
{
options.AsSafeMode();
});
方式 2:手動遷移
context.AutoMigrate(...);
方式 3:使用 Builder 模式
var runner = new MigrateBuilder<MyDbContext>(dbContext)
.WithOptions(new AutoMigrationOptions().AsSafeMode())
.Build();
await runner.ExecuteAsync();
五、生產環境最佳實踐
1. 強烈建議使用安全模式
options.AsSafeMode();
2. 不要直接修改列結構
請採用:
- 新增列
- 數據遷移
- 刪除舊列(開發或測試環境執行)
3. 注意數據庫大小寫敏感設置
問題描述: 直接在不存在的數據庫上使用 EF Core 遷移可能會生成字段不敏感的數據庫表,導致後續遷移時出現字段索引名稱重複等問題。
解決方案:
方案一:通過 Fluent API 配置(推薦)
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// 設置所有字符串列區分大小寫
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
foreach (var property in entityType.GetProperties())
{
if (property.ClrType == typeof(string))
{
property.SetCollation("SQL_Latin1_General_CP1_CS_AS"); // SQL Server
// 或者對於 MySQL: property.SetCollation("utf8mb4_bin");
// 或者對於 PostgreSQL: property.SetCollation("C");
}
}
}
}
方案二:通過數據註解
public class MyEntity
{
public int Id { get; set; }
[Column(TypeName = "varchar(255) COLLATE SQL_Latin1_General_CP1_CS_AS")]
public string Name { get; set; }
}
方案三:數據庫層面設置
在創建數據庫時指定區分大小寫的排序規則:
-- SQL Server
CREATE DATABASE MyDatabase COLLATE SQL_Latin1_General_CP1_CS_AS;
-- MySQL
CREATE DATABASE MyDatabase CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;
-- PostgreSQL
CREATE DATABASE MyDatabase ENCODING 'UTF8' LC_COLLATE 'C' LC_CTYPE 'C';
六、關於 DTO 過濾的設計思考
這個機制是根據大量實際項目經驗總結出來的。
在模塊化或 DDD 項目中,一個複雜模塊通常包含:
- 聚合根實體
- ValueObject
- 多個 DTO(讀模型、投影模型)
- 後台報表模型
但其中真正對應數據庫表的只有一部分。
如果所有類都被 EF Core 掃描並參與遷移——
- 遷移文件會變得巨大且無意義
- 數據庫會出現大量無用表
- 模塊之間可能因為 DTO 衝突導致遷移失敗
因此,AutoMigrate 內置過濾規則,可以説是模塊化數據庫建模中一個非常實用、工程化的功能點。
七、總結
Daibitx.EFCore.AutoMigrate 的核心價值在於:
- 讓 EF Core 的遷移機制更適合模塊化、多團隊協作的體系
- 提供清晰、可控、安全的遷移策略
- 解決 DTO/查詢模型誤導致數據庫結構污染的問題
- 對生產環境更友好
- 提供工程級的事務、安全保護與數據庫兼容性支持
如果你的架構包含:
- 多模塊
- 多 DbContext
- 多團隊協作
- 多環境(開發、測試、生產)
- 多數據庫適配
那麼 AutoMigrate 可能正是你需要的那一層“工程化補丁”。