如果需要從 .NET 8 升級至 .NET 10,我會將這項工作視為 API 合約遷移,而非僅僅是專案檔案的修改。一項服務可能可以編譯、通過單元測試,但仍然可能因 JSON 結構、狀態碼、驗證回應或 OpenAPI 文件的改變而讓消費者感到意外。
這個風險現在變得非常重要,因為 Microsoft 已確認 .NET 8 與 .NET 9 將於 2026 年 11 月 10 日結束支援。.NET 10 與 C# 14 是目前穩定的版本,而 .NET 10 是支援的 LTS 目標。
為什麼截止日期改變了我的升級順序
我的第一步是盤點,而非重新定位目標。我會列出所有可部署的專案、測試專案、global.json、容器基礎映像、CI SDK 固定版本,以及 Microsoft 套件參考。dotnet --list-sdks 可以顯示機器能建置哪些 SDK;dotnet --info 則顯示目前環境實際解析的版本。
如果需要更詳細的盤點資訊,我之前關於 dotnet sdk check 的指南是一個很好的起點。對於仍在使用 .NET 8 的 API,更廣泛的 Web API 設定與安全性檢查清單 可以幫助在遷移前找出需要保護的行為。
接著,我將遷移分為三個部分:SDK 與目標框架、NuGet 相依性,以及執行階段基礎架構。將這些變更分開呈現,可以讓問題更容易定位。一個大型的相依性更新提交可能很快完成,但卻很難診斷問題。
在合約測試保護下從 .NET 8 升級至 .NET 10
在變更 net8.0 之前,我會先針對消費者無法容忍改變的端點,加入一小組測試。我關注的是可觀察的行為:狀態碼、內容類型、必要的 JSON 名稱,以及驗證邊界。我避免斷言整個序列化字串,因為無害的屬性順序可能會讓測試變得雜亂。
以下是一個針對 Minimal API 的精簡 xUnit 測試:
using System.Net;
using System.Text.Json;
using Microsoft.AspNetCore.Mvc.Testing;
using Xunit;
public sealed class ProductContractTests(
WebApplicationFactory<Program> factory)
: IClassFixture<WebApplicationFactory<Program>>
{
[Fact]
public async Task GetProduct_keeps_the_public_contract()
{
using var client = factory.CreateClient(new()
{
BaseAddress = new Uri("https://localhost")
});
using var response = await client.GetAsync("/products/42");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(
"application/json",
response.Content.Headers.ContentType?.MediaType);
await using var stream =
await response.Content.ReadAsStreamAsync();
using var json = await JsonDocument.ParseAsync(stream);
Assert.Equal(
42,
json.RootElement.GetProperty("id").GetInt32());
Assert.True(json.RootElement.TryGetProperty("name", out _)));
}
}
Enter fullscreen mode Exit fullscreen mode
測試專案引用與應用程式相同 10.0 維護版本的 Microsoft.AspNetCore.Mvc.Testing。這個測試並非完整的相容性測試套件,而是一個範本,用來涵蓋最關鍵的合約:成功的讀取、驗證失敗、未授權請求,以及找不到資源的回應。這些檢查可以捕捉到單元測試在 HTTP 管線之下無法察覺的行為變化。
對於頂層 Minimal API,我也會將進入點暴露給測試專案:
app.Run();
public partial class Program
{
}
Enter fullscreen mode Exit fullscreen mode
Microsoft 維護了一份完整的 .NET 10 WebApplicationFactory 範例。我會以此作為測試主機設定的參考,而非自行建立自訂的伺服器管線。
同時移動執行階段、套件與管線
在基準測試通過後,我會將應用程式與測試專案變更為 net10.0。我會將 Microsoft.AspNetCore、Microsoft.Extensions 與 EF Core 套件更新至相容的 10.0 維護版本,然後查閱官方的 .NET 10 破壞性變更目錄,而非僅依賴編譯器錯誤來猜測。
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
</PropertyGroup>
Enter fullscreen mode Exit fullscreen mode
我在本機與 CI 中執行相同的簡短序列:
dotnet package list --outdated
dotnet restore
dotnet build --warnaserror
dotnet test
dotnet publish -c Release
Enter fullscreen mode Exit fullscreen mode
管線必須安裝 .NET 10 SDK,而容器化服務必須使用相對應的 10.0 建置與執行階段映像。僅更新開發人員的機器,對實際部署到生產環境的成品來說,意義不大。
OpenAPI 需要明確的決策。ASP.NET Core 10 內建的產生器預設會輸出 OpenAPI 3.1。如果現有的用戶端產生器只能理解 3.0,我會暫時固定格式,並將該合約變更另外排程:
builder.Services.AddOpenApi(options =>
{
options.OpenApiVersion =
Microsoft.OpenApi.OpenApiSpecVersion.OpenApi3_0;
});
Enter fullscreen mode Exit fullscreen mode
這個固定只是相容性工具,並非要永遠避免使用 OpenAPI 3.1。我只會保留它,直到消費者完成測試與升級為止。
何時不適合使用此直接路徑
當資料庫提供者、驗證元件、託管平台或商業相依性不支援 .NET 10 時,直接重新定位目標是錯誤的做法。在此情況下,我會先隔離阻礙因素。透過 .NET 9 進行遷移可以幫助以較小的步驟暴露變更,但 .NET 9 同樣有 2026 年 11 月的支援截止期限,因此它不是最終目標。
我也不會將執行階段升級與 EF 模型重新設計、驗證重寫或 OpenAPI 產生器替換結合在一起。這些可能都是值得做的事,但分開提交與部署可以保留有用的回滾邊界。
對於共用程式庫,暫時使用 net8.0;net10.0 多目標可以讓應用程式獨立遷移。對於可部署的 API,多目標並不能取代選擇與驗證生產環境將執行的執行階段。
在將服務移至 .NET 10 之前,您會先固定哪一項 API 合約?
感謝閱讀 ? 我們下次見。
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.