C# でパッケージのロックファイルを使う
◆ packages.config だと依存関係もフラットになってて分かりづらい
◆ 最近は PackageReference らしく こっちはトップレベルだけの管理
◆ ロックファイルを使うには PropertyGroup に RestorePackagesWithLockFile を追加すれば良いみたい
◆ 最近は PackageReference らしく こっちはトップレベルだけの管理
◆ ロックファイルを使うには PropertyGroup に RestorePackagesWithLockFile を追加すれば良いみたい
古いパッケージ管理
久々に C# プロジェクトを触ったのですがパッケージ操作が面倒でしたVS を使って管理しているもので VS 上の GUI で NuGet パッケージをインストールしているものです
インストールしているパッケージの一覧は packages.config という xml ファイルで管理されています
Text.Json をインストールしたものだとこういう感じです
[packages.config]
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Bcl.AsyncInterfaces" version="7.0.0" targetFramework="net48" />
<package id="System.Buffers" version="4.5.1" targetFramework="net48" />
<package id="System.Memory" version="4.5.5" targetFramework="net48" />
<package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net48" />
<package id="System.Runtime.CompilerServices.Unsafe" version="6.0.0" targetFramework="net48" />
<package id="System.Text.Encodings.Web" version="7.0.0" targetFramework="net48" />
<package id="System.Text.Json" version="7.0.3" targetFramework="net48" />
<package id="System.Threading.Tasks.Extensionsversion="4.5.4" targetFramework="net48" />
<package id="System.ValueTuple" version="4.5.0" targetFramework="net48" />
</packages>
一つ入れただけなのに全部の依存関係で入るパッケージがフラットで管理されています
入れたばかりならいいのですが 何年も前のものだと どれが自分で入れたのでどれが依存関係で入ったのかわかりません
覚えていてもひとつ不要になったときに依存関係をまとめて消せないです
「A ▶ B ▶ C」 という依存関係があったとします
A には B が必要で A を入れると B も C も入るという意味です
いきなり C を消そうとしても B が C を必要としているというエラーで消せません
A → B → C の順番で消さないといけないです
そもそも B を消そうにも B ってどのパッケージ?という状態です
自分で入れたトップレベルのパッケージが複数あると 依存関係で入ったことはわかってもどの依存関係で入ったかわかりません
依存関係も削除のチェックボックスとかあるといいのですけどね
とりあえず依存関係で使われてるものは消せないという状態なので トップレベルパッケージから不要なのを削除後に 残ってるパッケージに対して削除を試して消せたら消すを繰り返し 1 周の内に消せるのがなくなるまで続けるという荒業で解決しました
最近のパッケージ管理
dotnet コマンドで消したらいい感じに消せたりしなかったのかな と思ったりしましたが レガシーなもので .NET Framework を使ってるので dotnet コマンドでうまくいくのかもわからないですその辺を調べてみると 最近は packages.config ではなく PackageReference という方法でパッケージを管理するそうです
コマンドだけじゃなく管理の方法も .NET になってから新しくなってたんですね
VS2022 で試すと .NET Framework としてプロジェクトを作ると packages.config になって .NET (6.0) として作ると PackageReference になりました
PackageReference というのは専用ファイルがあるのではなく csproj の中のエントリです
Project の中に ItemGroup というのがあってその中に PackageReference がパッケージの数だけ並ぶ感じです
Text.Json をインストールしてみた例です
[ConsoleApp1.csproj]
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Text.Json" Version="7.0.3" />
</ItemGroup>
</Project>
トップレベルのパッケージだけが記載されています
見やすいですし どれが依存関係で入ったかわからない問題は起きないですね
VS 上の GUI のパッケージマネージャーでも「インストール済み」にはトップレベルだけが表示されるようなっていました
ソリューションエクスプローラーの表示もパッケージ管理方法によって変わっていて PackageReference の方法だとツリー形式で表示されます
ロックファイル
これを見て思う問題点はロックファイルがないことですトップレベルの情報しか持たなくなったので 依存関係で入ったパッケージのバージョンが固定できません
別の環境でインストールし直すとバージョンが変わってしまうかもしれません
ロックファイルを作る方法はないのかなと調べたらこういう記事がありました
https://devblogs.microsoft.com/nuget/enable-repeatable-package-restores-using-a-lock-file/
PropertyGroup の中に
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
を追加すれば良いようです
追加するとそれを VS が認識して自動で packages.lock.json というファイルを作ってくれました
中身はこういう感じです
{
"version": 1,
"dependencies": {
"net6.0": {
"System.Text.Json": {
"type": "Direct",
"requested": "[7.0.3, )",
"resolved": "7.0.3",
"contentHash": "AyjhwXN1zTFeIibHimfJn6eAsZ7rTBib79JQpzg8WAuR/HKDu9JGNHTuu3nbbXQ/bgI+U4z6HtZmCHNXB1QXrQ==",
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "6.0.0",
"System.Text.Encodings.Web": "7.0.0"
}
},
"System.Runtime.CompilerServices.Unsafe": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
},
"System.Text.Encodings.Web": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "OP6umVGxc0Z0MvZQBVigj4/U31Pw72ITihDWP9WiWDm+q5aoe0GaJivsfYGq53o6dxH7DcXWiCTl7+0o2CGdmg==",
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
}
}
}
}
シンプルでわかりやすいですね
パッケージの場所
packages.config の方法だと packages フォルダが作られてその中に各パッケージの dll などが配置されていましたNode.js の node_modules フォルダ的なものです
PackageReference では packages フォルダは作られず 共通の .nuget\packages フォルダの中に配置されます
ビルド時に obj や bin フォルダの中に必要なファイルがコピーされてるようです
Node.js の yarn pnp のグローバルキャッシュみたいな感じでしょうか
.NET では色々なところで .NET Framework より良くなってますね