.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はマシンがビルド可能なものを示し、dotnet --infoは現在の環境が実際に解決するものを示します。

インベントリにさらに詳細が必要な場合は、dotnet sdk checkに関する私の古いガイドが有用な出発点になります。.NET 8で動作しているAPIについては、Web APIのセットアップとセキュリティチェックリストが、移行前に保護すべき動作の特定に役立ちます。

その後、移行を3つの変更に分けます: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を参照します。この1つのテストは完全な互換性スイートではありません。最も重要な契約の少数のテンプレートです:成功した読み取り、検証の失敗、未承認のリクエスト、およびnot-found応答です。これらのチェックは、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契約を固定しますか?

お読みいただきありがとうございます。次回お会いしましょう。