1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

E02 【現場救急Tips】参照が壊れる NuGet競合の止血と恒久対策 ― またお前か

Last updated at Posted at 2026-01-15

連載Index(読む順・公開済(リンク)はここが最新): S00_門前の誓い_総合Index

依存事故が一番タチ悪いのは、「配置先に古いDLLが残っている」時だ。
コードは正しいし、NuGetの復元も通る。なのに起動しない。

その瞬間、犯人はソースではなく“混在”になる。
新旧が混ざった実行フォルダは、都合よく壊れる。

このページは、NuGet競合(復元時)だけでなく、
配置先に残ったDLL(実行時)まで含めて、止血と再発防止をセットでまとめる。

理由は、“同じはず”を破る要素が端末側に積まれているからだ。

  • nuget.config の差(参照しているフィード/sourceや優先順位が違う)
  • 認証/プロキシの差(社内フィードに繋がらず、取得経路が変わる)
  • キャッシュ/復元の残骸(古いパッケージや古い解決結果が残る)
  • SDK/VS/MSBuildの差(復元・ビルドの挙動や警告/エラー扱いが変わる)
  • 配布残骸(実行時だけ:古いdllが残って“新旧混在”になる)

……またお前か。依存関係のバージョン違いだ。

コードじゃない。コードじゃないんだ。。
まず 「どこで死んでるか」「何が選ばれたか」 を見ろ。
症状→原因→止血→恒久対策→再発防止を、迷子にならないチェック順でまとめます。


最短3分ルート(初心者はここだけやれ)

  1. ビルド出力/CIログで NUxxxx を探す(依存で死んでるかの当たり判定)
  2. dotnet restore -v minimal(.sln / .csproj の場所で)
  3. dotnet list <csproj> package --include-transitive(解決Version=確定情報)
  4. NU1107/NU1605 が出たら「上位でVersion宣言」して揃える(止血)
  5. 直らなければ dotnet cleandotnet nuget locals all --clear → restore/build

0. まずそれ、依存で死んでる?(当たり判定)

結論:依存事故かどうかは “エラーの出どころ” で判定する。
最初に「いま見えているエラーが、どこに出ているか」を決める。ここを外すと永遠に迷う。

  • CI/ローカルのビルド出力に出ているdotnet restore/build の出力を見ろ
  • アプリが起動して落ちる(実行時) → 例外ログ(アプリログ/イベントログ)を見ろ
  • 「端末によって結果が違う」 → “同じはずが同じになってない”前提で、解決結果と残骸を疑え

補足(Visual Studio)

  • NUxxxx / CSxxxx は、まず「エラー一覧」か「出力ウィンドウ(ビルド)」に出る
  • “どこで死んでるか”は、エラー一覧の分類(復元/ビルド/実行)でだいたい分かる

0-1. 端末差が出る因果(先に1枚で腹落ち)

0-2. 依存で死んでるサイン(どこに出るか込み)

どこを見てる? サイン(例) だいたい犯人 まずやる
CI/ローカルのビルド出力(VSの出力/ターミナル) NU1107 NU1605 NU1301 など NUxxxx NuGet(競合/ダウングレード/取得失敗) dotnet restore -v minimal
CI/ローカルのビルド出力(VSの出力/ターミナル) CS1705(参照アセンブリのVersion差) 参照バージョン不一致 dotnet list package --include-transitive
実行時ログ(アプリログ/Windowsイベントログ/ダンプ) Could not load file or assembly... / FileLoadException / MissingMethodException “古い/別のdll”をロード ロードされたdllの Location/Version を取る
現象(端末差/環境差) 「Aは動くのにBだけ死ぬ」 解決結果の差 / 配布残骸 / source差 解決結果残骸 を疑う

補足:

  • 「どこに出るか」が分からないなら、まずは ビルド出力(VSの出力 or ターミナル) に寄せる。そこが一番早い。
  • 本番で実行時に落ちるなら、まず アプリのログ。無ければ Windowsイベントログ(Application) を見ろ。

0-3. restore単体で当たりを出す(まず叫ばせろ)

失敗しているプロジェクト(.sln か .csproj のある場所)で叩く。
依存が原因なら、ビルドの前に restore が叫ぶことが多い。

dotnet restore -v minimal
  • ここで NUxxxx が出たら → 依存(NuGet)で死んでる。次へ。
  • ここが通って、ビルドで .cs の行を指して落ちる → まずコード。
  • restore は通るのに実行時だけ落ちる → 配布残骸/ロード差の可能性が濃い(後半へ)。

0-4. 解決されたバージョンを見る(ここが本丸)

「何を見るか」が曖昧だと迷子になる。見る対象を固定する。

dotnet list <失敗しているcsproj> package --include-transitive

ここで見るのは2点だけ。

  • 同じパッケージが複数バージョン要求されてないか
  • 最終的にどのバージョンが選ばれたか(=解決されたバージョン)

1. 用語を先に潰す(中央管理/競合/ロック/確定情報)

1-1. Directory.Packages.props って何?

一言で言うと 「ソリューション全体の NuGet パッケージVersion表」
.csproj に散らばった Version 指定を、1枚のファイルに集めて “全員を同じVersionにする” ために使う。

  • これは NuGet の仕組み(MSBuild + NuGet の復元処理が読む)
  • VS専用ではない(CLI dotnet restore でも効く)
  • 目的は「端末差」「プロジェクト差」で Version が揺れて壊れるのを止めること

1-2. 「競合しているパッケージ」ってどういう状態?

同じソリューション内で、同じパッケージに対して

  • ある依存は A >= 1.0 を要求
  • 別の依存は A >= 2.0 を要求

みたいに 要求Versionが割れている 状態。

この割れが表に出ると NU1107(競合)や NU1605(ダウングレード)が出る。
重要:あなたが直接入れたパッケージじゃなくても起きる(推移依存が揉める)。

1-3. ロック(packages.lock.json)って何?

一言で言うと 「今回復元で選ばれたパッケージVersion一式を固定する台帳」
次回から、その台帳と違う復元結果になるのを拒否できる。

  • “鍵”ではなく 固定(ロック) の意味
  • 目的は「昨日と今日で、復元結果が勝手に変わる」を止めること

1-4. このページで言う「確定情報」って何?

推測で殴ると時間が溶ける。
だから “いまこの端末で実際に選ばれたVersion/実体” を後で追える形で残す。

  • dotnet list package --include-transitive の結果(解決Version一覧)
  • obj/project.assets.json(解決結果の判決文(決文))
  • 実行時にロードされたdllの Location/Version(現物)

2. このページで使うコマンド(何をして、どこに出る?)

前提:ここで使うのは dotnet コマンド。
内部で NuGet(復元)MSBuild(ビルド) を呼び出している。

2-1. dotnet restore(復元)

何をする?

  • NuGetパッケージを取得し、依存関係を解決して、参照できる状態にする。

どこに出る?

  • コンソール/CIログに NUxxxx が出る
  • obj/project.assets.json が生成される(解決結果)

よく使う:

dotnet restore -v minimal

2-2. dotnet list package(解決結果の一覧)

何をする?

  • そのプロジェクトで「最終的に使われるパッケージVersion」を一覧する。
  • --include-transitive で「依存の依存」まで出す(揉めてるのはだいたいこっち)。
dotnet list <csproj> package --include-transitive

2-3. dotnet build(ビルド)

何をする?

  • コンパイルとリンク。ここで CS1705 など “参照Version差” が表に出ることがある。

2-4. dotnet clean / dotnet nuget locals(掃除)

何をする?

  • 残骸を消す。参照事故は残骸で再現が消えるので、途中で入れる価値がある。
dotnet clean
dotnet nuget locals all --clear

3. 困ったらここから(最短ルート)

  1. 復元(restore)で死ぬNUxxxx を見ろ(まず dotnet restore
  2. ビルド(build)で死ぬCS1705 等 “参照Version差” を見ろ
  3. 実行(run)で死ぬFileLoadException / MissingMethodException を“ロード元(パス)”で殺せ

4. 症状(まず分類)

どこで死ぬ 典型メッセージ まず取る確定情報
restore(復元) NU1107 NU1605 NU1301 restoreログ / project.assets.json
build(コンパイル) CS1705 失敗プロジェクト / dotnet list package
run(実行時) Could not load file or assembly... ロードdllの Location/Version
端末差が出る 「同じ成果物のはずなのに…」 解決結果の差分 / 配布残骸

5. 原因(結論)

依存事故の主犯は、だいたいこの4つ。

主犯 何が起きる
参照方式の混在 同名dllが二重に入り、どれがロードされたか分からなくなる
バージョンの揺れ 推移依存で別Versionが選ばれて端末差が出る
配布残骸 古いdllが残って“新旧混在”で実行時に爆発
ソース/キャッシュの汚れ 取れるパッケージが環境で変わる

まとめ(ここだけ持ち帰れ)

  • NUxxxx が出たら:まず dotnet restore -v minimal(依存で死んでる当たり判定)
  • 迷ったら:dotnet list <csproj> package --include-transitive(解決されたVersion=確定情報)
  • 実行時だけ死ぬなら:ロード元(Location/Version)をログ→配布は「上書き禁止・丸ごと入替」

関連リンク(回遊導線)

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?