一種更安全、可控的 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 類,例如:

  • UserDto
  • OrderQueryDto
  • ProductItemDTO

這些類只是:

  • 視圖模型
  • 讀模型
  • 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. 不要直接修改列結構

請採用:

  1. 新增列
  2. 數據遷移
  3. 刪除舊列(開發或測試環境執行)

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 可能正是你需要的那一層“工程化補丁”。