.NET MAUI を Visual Studio で開発していると、「Visual Studio を更新したのに .NET MAUI 9.0.30 が使われない」「Microsoft.Maui.Controls を手動で上げないといけないのはなぜ?」と混乱しがちです。この記事では、Visual Studio/.NET SDK/MAUI workload/NuGet の役割分担を整理し、更新作業を事故らせない実践手順までまとめます。
.NET MAUI 9.0.30 が Visual Studio 更新だけで反映されない“ように見える”理由
まず押さえるべきポイントは、.NET MAUI の更新には大きく分けて「開発環境(SDK/ツール)」と「プロジェクトが参照する NuGet パッケージ」という 2 系統があることです。Visual Studio の更新や .NET SDK の更新は、基本的に開発環境側(コンパイラ、MSBuild、テンプレート、workload など)を整える作業であり、既存プロジェクトの .csproj を勝手に書き換えて NuGet パッケージのバージョンを上げることはしません。
つまり、あなたのプロジェクトが Microsoft.Maui.Controls を明示的にバージョン固定している場合、その固定は Visual Studio を更新しても維持されます(自動更新されません)。一方で、プロジェクト側が明示固定していない場合は、利用される MAUI の既定バージョンは「インストールされている .NET SDK と MAUI workload の組み合わせ」に依存します。ここが「更新したはずなのに上がっていない」現象の主因です。
結論:Visual Studio 更新、MAUI workload 更新、NuGet 更新は別のレイヤー
混乱を断ち切るために、更新対象を表で分解します。
| レイヤー | あなたが更新するもの | 主な更新手段 | 更新されないもの(誤解ポイント) |
|---|---|---|---|
| IDE | Visual Studio 本体、デバッガ、テンプレート類 | Visual Studio Installer の更新 | 既存プロジェクトの NuGet バージョン固定 |
| .NET SDK | dotnet CLI / MSBuild / コンパイラ / ランタイム | .NET SDK のインストール(VS 付属 or 単体) | プロジェクトに書いた PackageReference のバージョン |
| MAUI workload | MAUI 用の追加 SDK/ツール群(プラットフォーム対応、ビルド用パックなど) | dotnet workload install/update、または VS の MAUI 開発ワークロード | プロジェクトが参照する NuGet パッケージの固定値 |
| NuGet(プロジェクト依存) | Microsoft.Maui.Controls などの参照パッケージ | NuGet パッケージマネージャ、dotnet add package、CPM | workload を更新しただけで勝手に .csproj が書き換わること |
Microsoft Q&A でも「workload の主な更新対象はツールや SDK で、NuGet パッケージ更新は workload の責務ではない」という趣旨で説明されています。つまり環境更新とプロジェクト更新を意識的に分けるのが正攻法です。
Microsoft.Maui.Controls とは何者か
Microsoft.Maui.Controls は、.NET MAUI で画面を作るときに中心になるUI コントロールと XAML 基盤を提供する NuGet パッケージです。ボタン、ラベル、レイアウト、ページ、ナビゲーション、データバインディング、テーマなど、いわゆる「MAUI アプリの UI 層」を構成する部品がまとまっています。
NuGet の説明にも、Microsoft.Maui.Controls が「UI controls と XAML infrastructure を提供する」ことが明記されています。言い換えると、MAUI アプリを作るうえで避けて通れない中核パッケージです。
なぜ PackageReference を手動で追加すると 9.0.30 を使えるのか
プロジェクトが参照する NuGet のバージョンは、最終的に「復元(restore)時の依存関係解決」で決まります。ここで .csproj に次のような指定を書くと、NuGet はそのプロジェクトでは 9.0.30 を優先的に使うように解決します。
<ItemGroup>
<PackageReference Include="Microsoft.Maui.Controls" Version="9.0.30" />
</ItemGroup>
この方法は単純で分かりやすい一方、MAUI は複数パッケージが連動して動くため、Controls だけを単独で上げると依存関係のズレ(ダウングレード警告など)が出ることがあります。特に Microsoft.Maui.Controls.Compatibility を併用しているケースでは、Controls 側とのバージョン不整合でエラー/警告になりやすいです。
公式の「.NET 8 → .NET 9 のアップグレード」手順でも、Microsoft.Maui.Controls を .NET 9 系に更新すること、そしてアプリが互換 API を使っていないなら Microsoft.Maui.Controls.Compatibility を外すことが案内されています。これは「関連パッケージをセットで整える」重要性を裏付けています。
暗黙参照(Implicit)を上書きするなら Update= も選択肢
MAUI のプロジェクトでは、SDK 側で暗黙に参照されるパッケージがあるため、上書き目的なら次のように Include ではなく Update を使う書き方が採られることがあります(「既にある参照のバージョンだけ変える」意図が明確になります)。
<ItemGroup>
<PackageReference Update="Microsoft.Maui.Controls" Version="9.0.30" />
</ItemGroup>
どちらが絶対正しいというより、チーム運用では「上書きであることが分かる」形に寄せると保守性が上がります(特に複数人で触るリポジトリ)。
Version="9.0.30" を書いたら workload も更新されるのか?
結論から言うと、更新されません。PackageReference の Version 指定は、あくまでそのプロジェクトが復元する NuGet パッケージのバージョン指定です。一方、workload は .NET SDK に追加インストールされるグローバルなツール/SDK パック群であり、更新は dotnet workload update(または Visual Studio Installer)で行います。両者は連動しません。
dotnet workload update は「インストール済みの workload を最新に更新する」コマンドで、NuGet.org から更新された workload manifest を確認し、ローカルの manifest を更新し、必要なパックの更新・旧版削除まで行います。つまり、workload の更新は workload のコマンドで完結します。
dotnet workload install maui を何度も実行すると問題になる?
基本的には「同じものを入れ直そうとしているだけ」になりやすく、冗長です。Microsoft Q&A でも「install は更新コマンドではなく、更新したいなら update を参照してほしい」という趣旨で案内されています。
ここで重要なのは、dotnet workload コマンドは特定の .NET SDK バージョンのコンテキストで動作する点です。複数の SDK が入っている環境では、どの SDK を使っているかで結果が変わり得ます(特に feature band が違うと挙動が変わる)。そのため「install を何度も打ったのに状況が変わらない」という場合、そもそも 参照している SDK が違う/固定されている可能性も疑うべきです。
正しい更新のやり方:install / update / repair / restore を使い分ける
workload 周りのコマンドは似ていますが役割が違います。実務で迷わないように、用途を表で整理します。
| コマンド | 主用途 | いつ使うべきか | 補足 |
|---|---|---|---|
dotnet workload install maui | MAUI workload の新規インストール | 未導入の環境に MAUI 開発環境を入れるとき | --source は NuGet ソースを上書き指定できる |
dotnet workload update | インストール済み workload を更新 | SDK を更新した後、または「更新あり」と出たとき | NuGet.org の manifest を参照して更新する |
dotnet workload repair | workload の修復 | インストール破損や不整合が疑われるとき | 環境トラブルの定番対処 |
dotnet workload restore | プロジェクトに必要な workload を復元 | CI や新規 PC セットアップ、リポジトリから復元するとき | 「このプロジェクトに必要な workload を揃える」用途 |
dotnet workload list | インストール状況の確認 | 「入ってる?」「更新ある?」の確認 | 更新通知(advertising)と合わせて使う |
更新したいなら install ではなく update。困ったら repair。CI や新規環境で「プロジェクトが動く状態に揃える」なら restore、という整理にしておくと、チーム内で手順がブレません。
--source https://api.nuget.org/v3/index.json を付ける意味
dotnet workload install は NuGet パッケージをダウンロードして workload を構成します。そのため、社内プロキシやプライベートフィード環境では「どの NuGet ソースを見るか」が問題になりがちです。--source を付けると、nuget.config に書かれているソース階層よりも優先して、指定したソースを使うようになります(複数指定も可能です)。
ただし、これは「どこから取得するか」の指定であって、プロジェクトの PackageReference の Version と連動するものではありません。workload を更新したいなら、同様に dotnet workload update -s ... を検討します。
「Visual Studio を更新したのに MAUI が古い」時にまず疑うべきこと
使っている .NET SDK が固定されていないか(global.json)
dotnet workload は「どの SDK バージョンで動いているか」に強く影響されます。リポジトリ直下に global.json があると、Visual Studio も CLI もその指定 SDK を優先するため、VS を更新しても、プロジェクトが古い SDK でビルドされ続けることがあります。結果として MAUI の既定バージョンも上がりません。
Visual Studio と CLI で見ている SDK が違わないか
同じ PC でも、ターミナル(PowerShell)から実行する dotnet と、Visual Studio が内部で使う SDK が一致していないと、workload の更新状況や復元結果が食い違って見えることがあります。まずは「どの SDK を使っているか」を dotnet --version と dotnet --info で揃えて確認し、必要なら SDK 自体の更新も行います。
実務で事故らない更新フロー(おすすめ手順)
現場での“更新作業あるある”は、どれか 1 つだけ上げて終わりにしてしまい、後日「ビルドは通るが特定端末で落ちる」「CI だけ失敗する」といったズレが出ることです。以下の順で揃えると、原因切り分けも簡単になります。
手順1:.NET SDK(必要なら Visual Studio も)を更新する
- .NET のアップグレードはまず SDK が中心(CLI/ビルド/ランタイムを含む)
- Visual Studio 経由で入るケースもあれば、単体インストーラを使うケースもある
手順2:MAUI workload を更新する
dotnet workload update
この段階で「MAUI のビルドに必要なツール群」を新しくします。更新は workload 更新コマンドで行う、という役割分担を徹底します。
手順3:プロジェクト側の NuGet パッケージを更新する
ここで初めて Microsoft.Maui.Controls などの NuGet バージョンを揃えます。重要なのは、プロジェクトが参照しているバージョンはプロジェクトに残るということです。Visual Studio や workload を更新しても、PackageReference の固定値は自動的には変わりません。
特に移行・互換目的で Microsoft.Maui.Controls.Compatibility を参照している場合は、公式案内に沿って「まだ必要か」を確認し、不要なら外すことで依存関係の衝突を避けられます。
手順4:初回ビルド前に bin / obj を削除して再ビルド
SDK/Workload/NuGet を一気に動かした後は、古い成果物が残っていると妙な警告や解決不能なエラーに見えることがあります。公式のアップグレード手順でも、初回ビルド前に bin と obj の削除が推奨されています。
「毎回手動でバージョンを変えたくない」場合の現実的な解決策
プロジェクトテンプレートを自分用(またはチーム用)に作る
Microsoft Q&A でも「毎回手動更新が嫌ならプロジェクトテンプレートを作る」という方向性が案内されています。新規プロジェクト作成直後に必ずやる作業(NuGet 更新、コード規約、CI 設定など)をテンプレート化すると、初動の差分が消えてレビューも楽になります。
テンプレートに含めておくと便利な例:
Microsoft.Maui.Controlsなどのバージョン固定(必要な場合のみ)- 社内 NuGet フィード利用時の
nuget.config配置方針 - CI 用の
dotnet workload restore実行手順 - Warning/Analyzer の統一設定
複数プロジェクトがあるなら「一括で揃える仕組み」を優先する
ソリューション内に複数の MAUI プロジェクトがある場合、プロジェクトごとにバージョン固定を散らすと、どこか 1 つだけ上げ忘れて依存関係が崩れます。運用としては「バージョンを一箇所で揃える(集中管理)」方向に寄せると保守が楽です。
ただし、ここはチームの規模や CI の設計によって最適解が変わる領域なので、まずは次の“最小ルール”から始めるのが安全です。
- 新規プロジェクト作成後、まず NuGet 更新が出ていないか確認する
- MAUI 関連パッケージは「同系列(同じ 9.0.xx)」で揃える
- workload 更新は
dotnet workload updateに統一し、install を更新目的で乱用しない
よくあるトラブルと対処(MAUI 更新時の実践メモ)
NU1605 など「パッケージのダウングレード検出」
MAUI 関連は依存が多いため、どれかだけ上げると「A は新しいが B は古い」状態になり、NU1605(ダウングレード検出)系の警告/エラーが出ることがあります。この場合は、
- 参照している MAUI 関連パッケージ(Controls / Compatibility など)を同系列に揃える
- 不要な Compatibility を削除できないか検討する
- 暗黙参照を上書きしているなら
Update=指定で意図を明確にする
といった方向で整理すると解決に近づきます。
workload が「入っているはずなのに認識されない/ビルドに必要と言われる」
この手の問題は「SDK コンテキストの違い」や「インストール破損・不整合」で起きがちです。まずは dotnet workload list で状態を確認し、改善しなければ dotnet workload repair を検討します。
まとめ:9.0.30 を“正しく”揃えるための考え方
- Visual Studio 更新と NuGet 更新は自動連動しない(既存プロジェクトは勝手に書き換わらない)
Microsoft.Maui.Controlsは MAUI の UI コントロールと XAML 基盤を提供する中核パッケージPackageReferenceのバージョン指定は「そのプロジェクトの依存関係」の話で、workload を更新しない- workload を更新したいなら
dotnet workload updateが基本(install ではない) - チーム運用では「SDK → workload → NuGet → bin/obj 削除 → 再ビルド」を手順化すると安定する

コメント