.NETのプロジェクトファイルをシンプルでDRYにする【その1】

はじめに

こんにちは、アーキテクトの小林です。

今回取り上げるプロジェクトファイルに関する話は、少々マニアックなテーマになります。実際の開発現場でプロジェクトファイルについて機能要件や非機能要件が出てくることはほぼ無く、現実として「意識しなくてもどうにかなる」類のものであるため、知る人ぞ知る知識になってくるかと思います。

とは言え、.NET Frameworkを.NETに移行するという状況になると、プロジェクトファイルの知識は必要不可欠です。 また、.NETへの移行はまだ先だとしても、プロジェクトファイルをシンプルでDRYにすると、運用・保守性が向上することは間違いありません。

この記事を読んでいただくことで、プロジェクトファイルの深淵に触れ、「あぁ、そういうことだったのね」とか「そんな方法もあったのか」という気持ちになっていただけるのではないかと思います。 なお、記事のボリュームが大きくなりすぎましたので、二回の記事に分けての紹介となりますが、ぜひお付き合いいただければと思います。

目次

.NET と .NET Frameworkのプロジェクトファイルの違いを知る

Visual Studio 2022で、.NET 8のコンソールアプリケーションと.NET Framework 4.8のコンソールアプリケーションを新しく作成してcsprojファイルを比較してみましょう。

左:.NET Framework 4.8/右:.NET 8

新しい.NET 8のプロジェクトファイルは、「SDKスタイル」と呼ばれる最新の形式で作られ、.NET Frameworkのプロジェクトファイルは、「非SDKスタイル」と呼ばれる旧形式で作られます。

非SDKスタイルは、Gitのコンフリクトでマージ作業をした人も多いと思います。この形式の特徴として、プロジェクトの所属ファイルがファイル内にすべて記述され、設定項目もたくさん記述されています。このため、並行開発を進める開発現場では、すぐに競合してしまいます。とても悩ましい形式であると思います。

一方、SDKスタイルは、ファイルの内容がスッキリしています。これは「デフォルトと同じ設定ならファイルに記述しない、設定を変更したらファイルに記述する」というルールを適用したことで、記述する内容を大幅に削ったものです。また、プロジェクトに所属するファイルも、ディレクトリ配下の特定の拡張子が自動的に組み込まれるため、大半のプロジェクトファイルは記述されなくなります。このため、プロジェクトファイルの変更機会が抑制されてチームで並行開発していてもコンフリクトすることが少なくなります。

「.NETと.NET Frameworkの違いでしょ?しょうがないよね」と思った方、もうちょっとお待ちください。 実は、一部の.NET Frameworkのプロジェクトは、SDKスタイルにすることができるのです。すでに存在する.NET FrameworkのプロジェクトをSDKスタイルに移行することもできますし、理由があって新しく.NET Frameworkのプロジェクトを作成する場合も、SDKスタイルにした方が良いです。

ということで、このあと、.NET FrameworkのプロジェクトをSDKスタイルに移行する方法をご紹介します。しかし、.NET FrameworkのプロジェクトをSDKスタイルに移行する前に知っておかなければいけないことと、やらなければいけないことがあります。それが、次の項で紹介するNuGetパッケージの参照方法の違いです。

非SDKスタイルプロジェクトにおけるNuGetパッケージの参照方法の違いを知る

.NET Frameworkで新しく作成したプロジェクトの既定のスタイルである「非SDKスタイル」では、NuGetパッケージの参照方法も旧方式と新方式があります。

これには名前がついていないので、公式ドキュメントの記載に沿って旧方式の「packages.config 方式」と新方式の「PackageReference方式」としましょう。

packages.config 方式は、packages.configというファイルと、packagesというフォルダを使ったNuGetパッケージの管理方式です。非SDKスタイルにおけるデフォルトの方式ですので、.NET Frameworkでプロジェクトを作成して使っているとほぼ「packages.config 方式」のNuGetパッケージの参照方法になっています。

package.config方式におけるプロジェクトファイルの記述内容

細かい説明は省きますが、この方式には以下の弱点があります。

  • 依存先パッケージが依存しているパッケージ(これを「推移的な依存関係」と呼びます)が動的に解決されません
  • PackageReference方式と比較してパッケージのインストールと更新が少なくとも5倍低速です

PackageReference方式は、プロジェクトファイル内だけで依存先パッケージの定義が完結しており、packages.configファイルと、packagesフォルダが不要になります。また、packages.config 方式の弱点が克服され、推移的な依存関係をすべて記載する必要がなくなり、インストールと更新の速度が改善されます。

PackageReference方式におけるプロジェクトファイルの記述内容

一部のパッケージの復元動作に問題があるそうですが、あまりお目にかかることの無い問題ですので、問題が起こった時に対処すれば良いと思います。これからはPackageReference方式を採用すべきかと思います。

ということで、次の項では、非SDKスタイルのプロジェクトのデフォルトをPackageReference方式にするようにVisual Studioの設定を変更する方法を紹介します。

非SDKスタイルプロジェクトのNuGetパッケージの参照方法のデフォルトをPackageReference方式に変更する

ここでは非SDKスタイルプロジェクトのNuGetパッケージの参照方法が、デフォルトでpackages.config 方式になっているVisual Studioの設定を変更して、PackageReference方式にする方法を紹介します。

Visual Studioを開いて、メニューの「ツール」...「オプション」...「NuGet パッケージ マネージャー」の「既定のパッケージ管理形式」を「PackageReference」に変更します。

既定のパッケージ管理形式をPackageReferenceに変更する

これで、新しい.NET Frameworkプロジェクトを作成したときにNuGetパッケージを追加すると、PackageReference方式が選択されるようになりました。

注意点として、すでにpackages.config が使われるプロジェクトでは、この設定に関わらずpackages.config 方式が選択されます。 次の項で紹介する手順で、packages.config 方式からPackageReference方式に移行してください。

packages.config 方式からPackageReference方式に移行する

ここでは非SDKスタイルプロジェクトのpackages.config 方式をPackageReference方式に移行する方法を紹介します。

ここで残念なお知らせがあります。ASP.NET のプロジェクトは、PackageReference方式をサポートしていません。ASP.NETのプロジェクトは、NeGetライブラリを更新したときに、web.configのアセンブリバインディングのリダイレクトを更新する仕組みが存在するのですが、これがPackageReference方式では機能しないためだそうです。

ASP.NET以外の以下のプロジェクトを移行してみましたが、いずれも問題なく移行できました。

  • Windows フォーム アプリケーション (.NET Framework)
  • WPF アプリ (.NET Framework)
  • コンソール アプリ (.NET Framework)
  • クラスライブラリ (.NET Framework)
  • 単体テスト プロジェクト (.NET Framework)

それでは、移行を試してみましょう。

  1. ソリューションエクスプローラーでpackage.configファイルを右クリックします。
  2. メニューから「package.config を PackageReference に移行する」を選択します。
    ソリューションエクスプローラーで右クリックするとメニューが表示される
  3. ダイアログが表示されるので「OK」をクリックします。
    PackageReferenceへの移行時のダイアログ
  4. 移行結果の画面が表示されて移行完了です。

もし、移行する際に推移的な依存関係がある場合は、以下のような画面になります。

推移的な依存関係がある場合のダイアログ

「最上位」というチェックボックスを付けると、直接的な依存先として移行することができます。 System.Threading.Task.ExtensionsをチェックしてOKを押した結果は以下のようになります。

最上位のパッケージを選択して移行した場合

「最上位のパッケージ」とは、プロジェクトが直接的にインストールしているNuGetパッケージで、「推移的なパッケージ」とは、間接的に依存しているNuGetパッケージとなります。

これでようやく、.NET FrameworkのプロジェクトをSDKスタイルプロジェクトに移行する準備が整いました。

非SDKスタイルプロジェクトをSDKスタイルプロジェクトに移行する

ここでは非SDKスタイルプロジェクトをSDKスタイルプロジェクトに移行する方法を紹介します。

こちらも残念なお知らせがあります。ASP.NET のプロジェクトは、公式ではSDKスタイルプロジェクトをサポートしていません。有志がつくったMSBuild.SDK.SystemWebというものがありますが、公式なものではありませんので、採用するのは難しいです。

ASP.NET以外の以下のプロジェクトを移行してみましたが、いずれも問題なく移行できました。

  • Windows フォーム アプリケーション (.NET Framework)
  • WPF アプリ (.NET Framework)
  • コンソール アプリ (.NET Framework)
  • クラスライブラリ (.NET Framework)
  • 単体テスト プロジェクト (.NET Framework)
    • 移行は成功しましたが、ソリューションエクスプローラーのアイコンがテストプロジェクトのアイコンになっておらず、<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />を追加する必要がありました。

SDKスタイルプロジェクトへの移行には、Visual Studio の拡張機能である「.NET Upgrade Assistant」のインストールが必要です。

メニューの「拡張機能」...「拡張機能の管理」で「オンライン」を選択し、「.NET Upgrade Assistant」を検索して、ダウンロードしてください。

.NET Upgrade Assistantを検索してダウンロード

ダウンロードが終わると、Visual Studioの再起動を促されますので、Visual Studioを閉じてください。 Visual Studioを閉じると、.NET Upgrade Assistantのインストーラーが起動します。

.NET Upgrade Assistantのインストール

.NET Upgrade Assistantのインストールが終わったら、移行を行いましょう。

  1. ソリューションエクスプローラーで移行したいプロジェクトを右クリックします。
  2. メニューから「Upgrade」を選択します。
    移行したいプロジェクトでUpgradeを選択する
  3. ダイアログが表示されるので「Upgrade project features」をクリックします。
    ダイアログが表示されるので下の「Upgrade project features」を選択
  4. ダイアログの画面が変わるので「Convert project to SDK style」をクリックします。
    「Convert project to SDK style」を選択
  5. 移行が始まりますのでしばらく待ちます。
    移行作業中のダイアログ表示
  6. 移行が完了すると、プロジェクトはSDKスタイルプロジェクトになっています。
  7. ソリューションエクスプローラーで移行したプロジェクトを右クリックすると「プロジェクトファイルの編集」という新しいメニューが追加されていますので選択します。
    プロジェクトファイルの編集メニューが選べるようになった
    .NET Framework 4.8のSDKスタイルプロジェクト

これで、コンフリクトの起きやすい.NET FrameworkのプロジェクトファイルをシンプルなSDKスタイルに変換できました。

まとめ

この記事では、.NETと.NET Frameworkのプロジェクトファイルの違いと、非SDKスタイルプロジェクトにおけるNuGetパッケージの参照方法の違いを説明し、.NET Frameworkのプロジェクトファイルを、シンプルなSDKスタイルプロジェクトに移行する方法を紹介しました。

プロジェクトファイルの違いと移行方法に関する組み合わせを表にまとめてみましたので、ご確認ください。

実装 スタイル NuGet 参照方法 備考
.NET Framework 非SDK packages.config 方式 .NET Frameworkのプロジェクトのデフォルト。
ソリューションエクスプローラーからPackageReference方式に移行可能。
.NET Framework 非SDK PackageReference方式 推移的な依存関係に対応。NuGetパッケージの復元が高速化。ASP.NETのプロジェクトは非対応。
.NET Upgrade AssistantでSDKスタイルに移行可能。
.NET Framework SDK PackageReference方式 プロジェクトファイルの記述がシンプルになりコンフリクトが軽減。ASP.NETのプロジェクトは非対応。
.NET Upgrade Assistantで.NETに移行可能。
.NET SDK PackageReference方式 .NET プロジェクトのデフォルト。

今回の記事は以上です。次回は、.NETプロジェクトファイルのDRY(Don't Repeat Yourself)に挑戦したいと思います。 この記事の続き「.NETのプロジェクトファイルをシンプルでDRYにする【その2】」は、近日公開予定です。

~ecbeingでは、.NET の知識を深めたいエンジニアを募集中です!~ careers.ecbeing.tech