第二十三章:升級策略與遷移指引
學習目標
- 掌握 ABP 版本升級的安全流程
- 實作資料庫遷移與回滾
- 理解從 ASP.NET Boilerplate 遷移的要點
- 設計向後相容性與 Breaking Changes 管理
先備知識
- 已完成第一至六章(基礎與基礎設施)
- 熟悉 Entity Framework Core migrations
- 了解 CI/CD 與版本控制
正文
一、ABP 版本升級流程
升級檢查清單
- 審視 Release Notes:識別 Breaking Changes 與新功能
- 備份資料庫:確保可恢復
- 在分支測試:不在主分支直接升級
- 測試關鍵功能:單元、整合測試應全數通過
- 演練在 Staging:與生產環境相同配置
- 逐步推出:先金絲雀、後全量
1.1 從 V10.0 升級至 V10.0 特別指南
ABP V10.0 是一個重大版本更新,包含以下關鍵變更:
.NET 10 升級:
- 您的解決方案必須升級至 .NET 10。
- 更新
global.json(若有) 與所有.csproj中的<TargetFramework>為net10.0。 - 更新 Dockerfile 中的 Base Image。
Mapperly 取代 AutoMapper:
- V10 預設使用 Mapperly。雖然 AutoMapper 仍受支援,但建議逐步遷移以獲得效能優勢。
- 若您沒有 AutoMapper 的商業授權,強烈建議遷移至 Mapperly。
EF Core 遷移:
- 升級後,請務必執行
Add-Migration,因為 OpenIddict 模組 (v7.x) 可能有資料庫結構變更。
- 升級後,請務必執行
升級命令
bash
# bash
# 檢查當前版本
dotnet package search Volo.Abp --exact-match | head -5
# 更新 ABP 套件
dotnet package update --filter Volo.Abp* --version 10.0.0
# 或手動修改 .csproj
# <PackageReference Include="Volo.Abp" Version="10.0.0" />升級後步驟
bash
# bash
# 1. 還原套件
dotnet restore
# 2. 編譯檢查相容性
dotnet build
# 3. 執行測試
dotnet test
# 4. 建立 migration
dotnet ef migrations add UpgradeToV10 -p src/MyApp.EntityFrameworkCore -s src/MyApp.DbMigrator
# 5. 在開發環境測試遷移
dotnet ef database update -p src/MyApp.EntityFrameworkCore -s src/MyApp.DbMigrator
# 6. 檢查遷移指令碼(務必審視!)二、資料庫遷移策略
漸進式遷移(推薦)
bash
# bash
# 步驟 1:在測試環境完整測試新 migration
dotnet ef database update --connection "TestConnection"
# 步驟 2:驗證資料完整性
# 執行檢查查詢...
SELECT COUNT(*) FROM Books; -- 應與舊系統一致
# 步驟 3:在 Staging 演練完整升級 + 回滾
dotnet ef database update
# ... 驗證應用功能 ...
dotnet ef database update --target PreviousMigration # 回滾
# 步驟 4:生產環境 - 先備份後升級
# mysqldump -u root -p bookstore > backup_$(date +%Y%m%d).sql
dotnet ef database update --connection "ProductionConnection"自訂 Migration 處理複雜邏輯
csharp
// csharp - Migrations/20251119_AddBookCategory.cs
public partial class AddBookCategory : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
// 新增欄位
migrationBuilder.AddColumn<string>(
name: "Category",
table: "Books",
type: "nvarchar(max)",
nullable: true);
// 資料遷移:根據現有欄位值設定新欄位
migrationBuilder.Sql(@"
UPDATE Books
SET Category = CASE
WHEN Price > 100 THEN 'Premium'
WHEN Price > 50 THEN 'Standard'
ELSE 'Budget'
END
");
// 設為非 NULL
migrationBuilder.AlterColumn<string>(
name: "Category",
table: "Books",
type: "nvarchar(max)",
nullable: false);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(name: "Category", table: "Books");
}
}三、多租戶環境下的資料庫遷移
為每租戶執行遷移
bash
# bash - 指令碼:migrate-all-tenants.sh
#!/bin/bash
TENANTS=("tenant1" "tenant2" "tenant3")
for tenant in "${TENANTS[@]}"; do
echo "Migrating $tenant..."
# 設定租戶特定連線字串
CONNECTION="Server=db-server;Database=bookstore_$tenant;User Id=sa;Password=Pass"
dotnet ef database update \
--connection "$CONNECTION" \
-p src/MyApp.EntityFrameworkCore \
-s src/MyApp.DbMigrator
if [ $? -eq 0 ]; then
echo "$tenant migrated successfully"
else
echo "ERROR: Failed to migrate $tenant"
exit 1
fi
done
echo "All tenants migrated!"四、向後相容性與 Breaking Changes
版本號規則(SemVer)
- Major(主版本):破壞性變更(如 API 移除、參數改變)
- Minor(次版本):新功能,向下相容
- Patch(修訂版本):修復,向下相容
處理 Breaking Changes
csharp
// csharp - 升級前的做法(已過時)
public class BookAppService
{
[Obsolete("使用 GetBookPagedAsync 代替", error: true)]
public async Task<List<BookDto>> GetBooksAsync()
{
return await GetBookPagedAsync(0, 100);
}
// 新方法
public async Task<PagedResultDto<BookDto>> GetBookPagedAsync(int skipCount, int maxResultCount)
{
// ...
}
}使用 Feature Toggle 漸進遷移
csharp
// csharp
public class BookAppService
{
private readonly IFeatureChecker _featureChecker;
public BookAppService(IFeatureChecker featureChecker)
{
_featureChecker = featureChecker;
}
public async Task<IEnumerable<BookDto>> GetBooksAsync()
{
// 根據 Feature 使用新舊方法
if (await _featureChecker.IsEnabledAsync("UseNewBookApi"))
{
return await GetBooksV2Async(); // 新邏輯
}
return await GetBooksV1Async(); // 舊邏輯
}
}五、從 ASP.NET Boilerplate 遷移
遷移評估
- 識別使用的 ABP v2 或 Boilerplate 版本
- 列舉自訂擴充與第三方模組
- 評估功能差異(某些功能社群版可能無)
逐模組遷移策略
Step 1:建立新 ABP Framework 專案
bash
# bash
abp new MyApp -t appStep 2:遷移 Domain 層
csharp
// csharp - 舊 Boilerplate 實體
public class Book : Entity<Guid>
{
public string Title { get; set; }
}
// csharp - 新 ABP 實體
public class Book : FullAuditedAggregateRoot<Guid>
{
public string Title { get; set; }
public Book() { }
public Book(Guid id, string title) : base(id) => Title = title;
}Step 3:遷移 Application 層
csharp
// csharp - 舊 Boilerplate AppService
public class BookAppService : ApplicationService, IBookAppService
{
private readonly IBookRepository _bookRepository;
public BookAppService(IBookRepository bookRepository)
{
_bookRepository = bookRepository;
}
}
// csharp - 新 ABP AppService(基本相同)
public class BookAppService : ApplicationService, IBookAppService
{
private readonly IRepository<Book, Guid> _bookRepository;
public BookAppService(IRepository<Book, Guid> bookRepository)
{
_bookRepository = bookRepository;
}
}Step 4:資料庫遷移
使用 ETL 工具或自訂指令碼逐表遷移
sql
-- sql - 遷移 Books 表
INSERT INTO NewSystem.Books (Id, Title, Author, CreationTime, CreatorId)
SELECT Id, Title, Author, CreationTime, CreatorId
FROM OldSystem.dbo.Books
WHERE Id NOT IN (SELECT Id FROM NewSystem.Books);
-- 驗證計數
SELECT COUNT(*) FROM OldSystem.dbo.Books;
SELECT COUNT(*) FROM NewSystem.Books;Step 5:雙寫驗證(過渡期)
csharp
// csharp - 在遷移期間同時寫入新舊系統
public class DualWriteBookService
{
private readonly OldBookService _oldService;
private readonly NewBookService _newService;
public async Task CreateBookAsync(CreateBookDto input)
{
var oldResult = await _oldService.CreateAsync(input);
var newResult = await _newService.CreateAsync(input);
if (oldResult.Id != newResult.Id)
throw new InvalidOperationException("Dual-write mismatch");
}
}六、回滾方案
快速回滾檢查清單
- [ ] 備份資料庫已驗證可還原
- [ ] 舊版本 Docker 映像仍可用
- [ ] 資料庫回滾指令已測試
- [ ] 通訊錄與回滾負責人已確認
Kubernetes 藍綠部署回滾
bash
# bash
# 部署失敗時立即回滾
kubectl rollout undo deployment/myapp
# 查看回滾歷史
kubectl rollout history deployment/myapp
# 回滾至特定版本
kubectl rollout undo deployment/myapp --to-revision=5資料庫回滾
bash
# bash
# 1. 停止應用
kubectl scale deployment myapp --replicas=0
# 2. 還原資料庫備份
mysql -u root -p bookstore < backup_20251119.sql
# 3. 恢復舊版本應用
kubectl set image deployment/myapp myapp=myregistry/myapp:9.3.0
kubectl scale deployment myapp --replicas=3
# 4. 驗證應用正常
curl http://myapp-svc/health七、升級檢查清單
升級前準備
- [ ] 備份資料庫與完整系統
- [ ] 審視升級版本的 Release Notes
- [ ] 檢查依賴套件相容性(Volo.Abp、EF Core 等)
- [ ] 在分支建立升級分支
測試階段
- [ ] 本地完整編譯與測試
- [ ] 執行所有自動化測試(單元 + 整合 + E2E)
- [ ] 手動測試關鍵業務流程
- [ ] 檢查資料庫遷移指令碼邏輯
Staging 驗證
- [ ] 部署至 Staging 環境
- [ ] 執行 smoke tests 與回歸測試
- [ ] 驗證資料遷移完整性
- [ ] 測試回滾流程
生產部署
- [ ] 建立 incident 標籤與溝通管道
- [ ] 先金絲雀:部署至 1-2 伺服器,監控 24 小時
- [ ] 全量部署:逐批升級剩餘伺服器
- [ ] 監控應用指標、日誌、錯誤率
- [ ] 準備回滾方案隨時執行八、實務最佳實務
- 升級前充分測試,不要過於激進
- 使用 Feature Toggles 管理新舊功能切換
- 保持舊版本可用以便快速回滾
- 文件化所有升級步驟與破壞性變更
- 社群活躍項目通常升級風險更低
- 定期升級,避免跨度太大的版本跳躍
實作練習
- 執行一次完整升級流程(本地 → Staging → 回滾)
- 建立複雜資料庫遷移,包含資料轉換邏輯
- 設計多租戶環境下的升級策略
- 評估與規劃一次遷移(如 Boilerplate → ABP Framework)
習題(至少 6 題)
概念題(易)
- SemVer 中的主、次、修訂版本各代表什麼?(難度:易)
- 為何升級前應備份資料庫?(難度:中)
計算 / 練習題(中) 3. 設計 50 個租戶的升級策略,評估時間與風險。(難度:中) 4. 比較三種遷移方式(停機升級、漸進升級、藍綠部署),各自的優缺點。(難度:中)
實作 / 編碼題(較難) 5. 實作完整的資料庫遷移(含複雜資料轉換、驗證、回滾)。(難度:較難) 6. 設計與實作多租戶系統的升級流程(含 Feature Toggle、回滾、監控)。(難度:較難)
術語表
- Breaking Change(破壞性變更):修改會導致既有代碼無法運行
- Migration(遷移):資料庫結構或應用版本的轉換
- Rollback(回滾):恢復至先前版本或資料狀態
- Canary Deployment(金絲雀部署):先部署新版本至小部分用戶進行驗證
參考資料
- ABP Upgrade Guide:https://docs.abp.io/en/abp/latest/ (來源:content7)
- Entity Framework Core Migrations:https://learn.microsoft.com/en-us/ef/core/managing-schemas/migrations/
- Semantic Versioning:https://semver.org/
習題解答:content/solutions/ch23-solutions.md