Skip to content

MS_NuGetPackageManagement

nishi_74322014 edited this page Sep 1, 2026 · 1 revision

NuGet を使用したパッケージ管理

NuGet の操作

GUI での操作

Microsoft Learn を参照。

CUI での操作

Microsoft Learn を参照。

移行メモ(表記): 原文の「doscs、learnを参照。」は
**docs(現 Microsoft Learn)**の入力誤りと判断し、
統合して「Microsoft Learn を参照。」に修正した。

補足(現在の操作手段/最新化): 本ページの記述は
packages.config + nuget.exe + パッケージ マネージャー コンソール
を前提としている。現在は次の 3 系統がある。

手段 対象 備考
dotnet CLI SDK スタイル プロジェクト 現在の第一候補dotnetコマンド
パッケージ マネージャー UI 両方 Visual Studio の GUI
パッケージ マネージャー コンソール(PowerShell) 主に packages.config Install-Package
nuget.exe packages.config レガシー
dotnet add package Newtonsoft.Json --version 13.0.3
dotnet remove package Newtonsoft.Json
dotnet list package --outdated          # 更新可能なものを列挙
dotnet list package --vulnerable        # 脆弱性のあるものを列挙
dotnet restore

dotnet list package --vulnerable は、
依存パッケージの既知の脆弱性を検出する。
CI に組み込んでおくと、サプライ チェーン上のリスクを継続的に検知できる。

アセンブリ参照先の種類

GAC (Global Assembly Cache)

GAC に登録されるような従来のMicrosoft Windows Installer
配布されるようなパッケージは、今までどおり GAC に登録されているものを参照する。

NuGet

GAC に登録されていないパッケージ (OSS ライブラリなど) は、
もちろん個別にパッケージをダウンロードし、それを参照することもできる。
しかし、そのパッケージが NuGet に登録されていれば、
NuGet のメリットを享受するために NuGet 経由でインストールするのが良いのではないかと思われる。

それ以外

GAC にも NuGet にも登録されていないパッケージを使用する場合は、
個別にダウンロードし、プロジェクトから参照させる。

注意事項

ローカルコピー

NuGet でインストールしたパッケージ (*.dll) は、GAC には含まれない。
このため、NuGet でインストールしたパッケージをアプリケーションで使用する場合、
「ローカルコピー」は必ず「True」にしておくこと。

補足(.NET Core 以降では GAC が無い/最新化): 本節の 3 分類は
.NET Framework 前提である。

.NET Framework .NET Core / .NET
GAC あり(共有配置) 廃止
参照の解決 GAC → bin の順 deps.json に従う
ローカルコピー 明示的に True にする必要 既定でアプリ配下に出力
フレームワーク マシンに 1 つ(の系列) アプリごと(SCD なら同梱)

GAC 廃止の意味は大きく、
「マシン全体で 1 つの版を共有する」という前提そのものが無くなった。
これにより、

  • アプリごとに異なる版のライブラリを使える(DLL Hell の解消)
  • 管理者権限なしで配置できる
  • アンインストール漏れによる残骸が生じない

という利点が得られた
(詳細は .NETアセンブリ / アセンブリ)。

ただし .NET Core でも、
ビルド出力に NuGet 参照がコピーされないという別の現象がある
(後述の.NET Core の場合を参照)。

もし、こんなことをしてしまった場合はどうなる?

補足(この節は packages.config 固有): 以下は、
packages.config 方式で「参照設定 / packages.config / packages フォルダ」
の 3 つが二重管理になっている
ことから生じる問題群である。

【packages.config 方式】3 箇所を整合させる必要がある

   .csproj の <Reference><HintPath>   ← 参照設定
   packages.config                    ← 入れたパッケージの一覧
   packages\ フォルダ                 ← 実体

【PackageReference 方式】1 箇所だけ

   .csproj の <PackageReference>      ← これだけ
   (実体はグローバル パッケージ フォルダ %USERPROFILE%\.nuget\packages)

PackageReference に移行すれば、本節の問題はほぼすべて消える
既存プロジェクトの移行方法は後述の Package Reference を参照。

手動で、NuGet でインストールしたパッケージの参照設定を解除した

ビルド

  • 参照設定を解除したパッケージを使用していない場合は、通る。
  • (ただし、参照設定、packages.config、packages フォルダの整合性は崩れたまま)

Install-Package

  • 既にインストールされています。というメッセージが表示され、変わりなし。
  • (ただし、参照設定、packages.config、packages フォルダの整合性は崩れたまま)

Update-Package

  • 当該パッケージに更新がなかった場合

    • 更新はありません。というメッセージが表示され、変わりなし。
    • (参照設定、packages.config、packages フォルダの整合性は崩れたまま)
  • 当該パッケージに更新があった場合

    • 問題なく更新が行われる。
    • 参照設定、packages.config、packages フォルダの整合性も直る。

Uninstall-Package

  • packages.config からは削除されるが、
    packages フォルダから削除する際に、正常に削除できなかった旨の警告が出る。
  • Visual Studio を再起動し、プロジェクトを再度開くと、
    そのとき packages フォルダからパッケージが削除される。
  • 参照設定、packages.config、packages フォルダの整合性も直る。

packages フォルダから、手動でフォルダを消した

消したパッケージが自動的に復元され、ビルドは正常終了する。

パッケージの復元は、Visual Studio や、MSBuild でビルドした際、
足りないパッケージを自動的に NuGet サイトからダウンロードする機能である。

パッケージの復元を行うための設定

  • Visual Studio の [ツール]-[オプション] でオプション画面を開く。
  • ツリーの中から、[NuGet パッケージ マネージャー]-[全般] を選択する。
  • 「足りないパッケージをダウンロードすることを NuGet に許可」にチェックを入れる。

さらに、MSBuild などのコマンドラインツールでのビルド時に、
足りないパッケージをダウンロードするには、以下の設定を行う。

  • Visual Studio の [ソリューション エクスプローラー] を右クリックし、
    「Enable NuGet Package Restore」を選択する。

これにより、ソリューションフォルダ直下に**「.nuget」**フォルダができ、
コマンドラインツールでのビルド時でも、足りないパッケージがあれば、
ダウンロードしてくれる(MSBuild だけでなく、devenv によるビルドでも有効)。

この動作は、Visual Studio 2015 (NuGet 2.7) で変更されている。

packages フォルダそのものを削除した

上記と同様、パッケージが復元され、ビルドは正常終了する。

補足(.nuget フォルダ方式は廃止済み): 上記の
「Enable NuGet Package Restore」による .nuget フォルダ方式
(MSBuild 統合による復元)は、NuGet 2.7 で非推奨
になった
(後述の Visual Studio 2015)。

現在は **「自動復元」**が既定で、
ビルドの前に NuGet 自身が復元を行うため、
.nuget\NuGet.targets をプロジェクトに取り込む必要はない。

# CI などで明示的に復元する場合
dotnet restore          # SDK スタイル
nuget restore Foo.sln   # packages.config 系
msbuild -t:restore      # MSBuild 統合

古いリポジトリを開いたときに .nuget フォルダが残っていたら、
マイグレーションが済んでいないサインである。

packages.config を手動で編集した

<package> タグを消した

  • Install-Package
    成功 (ふたたび packages.config に <package> が生成される)
  • Update-Package
    失敗 (パッケージがインストールされていないとのメッセージが表示される)
  • Uninstall-Package
    警告 (packages フォルダから削除する際に、正常に削除できなかった旨の警告が出る。
    Visual Studio を再起動し、プロジェクトを再度開くと、
    そのとき packages フォルダからパッケージが削除される)

バージョン番号を編集した

  • 存在するバージョン番号の場合
    ビルドは成功し、編集したバージョン番号のパッケージが packages フォルダに格納される。
    このとき、編集前のバージョンのパッケージは packages フォルダからは削除されない。
    このため、以下の不整合が起きる。

    • packages フォルダに、バージョンの異なる 2 つのパッケージが混在する。
    • 参照設定も解除されないので、プロジェクトは編集前のバージョンのパッケージを参照し続ける。
    • このため、packages.config に書かれたバージョン番号と、
      実際に参照しているバージョン番号が一致しないことになる。
  • 存在しないバージョン番号の場合
    「NuGet パッケージの復元がプロジェクト {プロジェクト名} に対して失敗しました」
    というメッセージは表示されるが、

    • ビルド自体は成功する(編集前のバージョン番号のパッケージの削除が行われないため。)。
    • この場合も、以下の不整合が起きる。
      • packages フォルダには、編集前のバージョン番号のパッケージのみが残る。
      • 参照設定も解除されないので、プロジェクトは編集前のバージョンのパッケージを参照し続ける。
      • このため、packages.config に書かれたバージョン番号と、
        実際に参照しているバージョン番号が一致しないことになる。

補足(この一連の検証の結論): 原文が丹念に検証している通り、
packages.config を手で編集しても、参照設定(.csproj)は追従しない
結果、**「宣言と実体がずれたままビルドが通る」**という
最も厄介な状態が生まれる。

見分け方:

# .csproj の HintPath と packages.config のバージョンを突き合わせる
grep -o 'packages\\[^\\]*\\' *.csproj | sort -u
grep -o 'version="[^"]*"' packages.config | sort -u

確実に直す手順は、原文の検証結果に沿えば
Uninstall-PackageInstall-Package(または Update-Package)である
(どちらも 3 者の整合性を回復させる)。

根本的な対処は PackageReference への移行で、
こちらは .csproj の 1 箇所しか無いため、ずれようがない。

同様に参照設定との整合性が崩れる。

  • プロジェクトファイル(.csproj、.vbproj)内の<HintPath></HintPath>のタグを修正する。
  • 若しくは、Uninstall-Package -> Install-Package を行う。

その他のトピック

package バージョンの変更時の注意事項

注意事項

NuGet は、Install-Package を行った際に、バージョン間の問題を解決するために、
"assemblyBinding -> dependentAssembly -> bindingRedirect" を追加する。

このため、

  • Update-Package
  • Uninstall-Package -> Install-Package

によって package のバージョンを変更した場合、問題を起こすことがある。

この場合、一度、assemblyBinding section を削除した後に、
Add-BindingRedirect を実行して、bindingRedirect を再生成する。

補足(bindingRedirect とは何か): .NET Framework は
参照したアセンブリの版が完全一致することを要求する
アセンブリ の厳密名による強いバインド)。
このため、

あなたのアプリ ──> LibA 1.0 ──> Newtonsoft.Json 9.0
             └───> LibB 2.0 ──> Newtonsoft.Json 13.0

  → LibA は 9.0 を要求するが、実際に置かれているのは 13.0
  → 「ファイルまたはアセンブリを読み込めませんでした」で落ちる

という DLL Hell が起きる。
これを「9.0 の要求は 13.0 で満たす」と読み替えさせるのが
bindingRedirectapp.config / web.config)である。

<dependentAssembly>
  <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" />
  <bindingRedirect oldVersion="0.0.0.0-13.0.0.0" newVersion="13.0.0.0" />
</dependentAssembly>

NuGet がこれを自動生成するため、
パッケージのバージョンを上げ下げすると古い記述が残って矛盾する——
というのが原文の指摘する問題である。
対処(section を消して Add-BindingRedirect で作り直す)は現在も有効。

.NET Core 以降では、この問題自体が存在しない
バインドの解決が deps.json に基づき、
**「参照より新しい版があればそれを使う」**という緩やかな規則になったため、
bindingRedirect は不要になった。
.NET Framework から移行する動機の一つがここにある。

参考

NuGetパッケージの DL 先の変更方法

変更方法

  • nuget.config を用意し、親ディレクトリに配置、
    ここに、repositoryPath を指定することで NuGet パッケージの DL 先を変更できそう。
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <config>
    <add key="repositoryPath" value=".\sharedpackages" />
  </config>
</configuration>
  • なお、検証の結果、ソリューション分割によって、
    NuGet パッケージの DL 先(packages)が分割される模様。
    (既定では *.sln ファイルと同じ階層に packages フォルダができる)

  • 従って、このケースで packages を共有したい場合、

    • 2つのソリューションのルート・ディレクトリに nuget.config を配置するか、
    • 2つのソリューションの其々の上位ディレクトリに nuget.config を配置し、
      同じ repositoryPath を指すか、

で、対応ができる。

  • 以下は、2つの *.sln ファイル(ClassLibrary.sln、ConsoleApp1.sln)間で
    packages を共有した例。
フォルダ/ファイル名                                                                    種類          
-----------------------------------------------------------------------------------------------------
Root1                                                                                  <Dir>         
 ├ ClassLibrary                                                                       <Dir>         
 |  ├ ClassLibrary1                                                                  <Dir>         
 |  |  ├ Properties                                                                 <Dir>         
 |  |  |  └ AssemblyInfo.cs                                                             <File>   
 |  |  ├ Class1.cs                                                                       <File>   
 |  |  ├ ClassLibrary1.csproj                                                            <File>   
 |  |  └ packages.config                                                                 <File>   
 |  ├ ClassLibrary2                                                                  <Dir>         
 |  |  ├ Properties                                                                 <Dir>         
 |  |  |  └ AssemblyInfo.cs                                                             <File>   
 |  |  ├ Class1.cs                                                                       <File>   
 |  |  ├ ClassLibrary2.csproj                                                            <File>   
 |  |  └ packages.config                                                                 <File>   
 |  └ ClassLibrary.sln                                                                    <File>   
 ├ ConsoleApp1                                                                        <Dir>         
 |  ├ ConsoleApp1                                                                    <Dir>         
 |  |  ├ Properties                                                                 <Dir>         
 |  |  |  └ AssemblyInfo.cs                                                             <File>   
 |  |  ├ App.config                                                                      <File>   
 |  |  ├ ConsoleApp1.csproj                                                              <File>   
 |  |  ├ packages.config                                                                 <File>   
 |  |  └ Program.cs                                                                      <File>   
 |  └ ConsoleApp1.sln                                                                     <File>   
 ├ sharedpackages                                                                     <Dir>         
 |  └ Newtonsoft.Json.10.0.3                                                         <Dir>         
 |      ├ lib                                                                        <Dir>         
 |      ├ tools                                                                      <Dir>         
 |      ├ LICENSE.md                                                                      <File>   
 |      └ Newtonsoft.Json.10.0.3.nupkg                                                    <File>   
 └ nuget.config                                                                            <File>   

補足(PackageReference では不要になる): この
「ソリューションごとに packages が分裂して重複ダウンロードされる」
という問題も、PackageReference 方式では発生しない

packages.config PackageReference
実体の置き場所 .sln ごとの packages\ ユーザー単位のグローバル フォルダ
既定のパス <sln>\packages %USERPROFILE%\.nuget\packages
重複 ソリューション数だけ増える 1 つを共有
設定キー repositoryPath globalPackagesFolder
<!-- PackageReference 方式でグローバル フォルダを変える場合 -->
<configuration>
  <config>
    <add key="globalPackagesFolder" value="D:\nuget-cache" />
  </config>
</configuration>

repositoryPath は PackageReference では効かないため、
移行後も古い設定が残っていると混乱のもとになる。

なお、CI で毎回ダウンロードしたくない場合は、
NUGET_PACKAGES 環境変数でキャッシュ場所を指定し、
ビルド エージェント間で共有するのが定石である。

参考

マイグレーション

Visual Studio 2015

NuGet 自体のバージョンを(NuGet 2.7 以降に)上げると、リストア方法も変更になる。

この場合、以下のように、リストア方法をマイグレーションする必要がある。

参考

Visual Studio 2017

Package Reference

Visual Studio 2017 からは、packages.config ではなく、
Project ファイルに統合された Package Reference を使用できる。

ただし、一部問題を観測している。

補足(移行手順/最新化): 現在は
packages.config → PackageReference の移行が推奨されており、
Visual Studio に移行機能が用意されている。

ソリューション エクスプローラーで packages.config を右クリック
  → [packages.config を PackageReference に移行する]

移行によって得られるもの:

効果 内容
3 者の二重管理が消える .csproj 1 箇所になる
推移的依存が明示されなくなる 直接使うものだけ書けばよい
ディスク使用量の削減 グローバル フォルダを共有
bin の肥大化が減る 必要なものだけ出力
dotnet CLI が使える CI がシンプルになる

注意点:

  • install.ps1 / uninstall.ps1 を持つパッケージは動かない
    (PackageReference は PowerShell スクリプトを実行しない)
  • content フォルダで設定ファイルを配るパッケージも同様
    contentFiles への対応が必要)
  • 推移的依存が自動で解決されるため、
    これまで明示されていた依存が .csproj から消える
    (原文が .NET Core の場合 で触れる NU1701 等の
    挙動差にもつながる)

原文が指す「一部問題」(CS0246)も、
この推移的依存の扱いの違いに起因するものである。

.NET Core の場合

  • .NET Core で、ビルド出力に NuGet リファレンスがコピーされない。

    • 以下を csproj ファイルに追加すると NuGet リファレンスをビルド出力にコピーする。
<PropertyGroup>
  <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>

補足(CopyLocalLockFileAssemblies が要る理由): .NET Core では、
dotnet build の出力(bin\)に依存 DLL が入らないことがある。
これは「実行時に deps.json を見てグローバル フォルダから解決する」
という設計のためで、不具合ではない

【dotnet run / dotnet publish】
   deps.json 経由で解決される → 問題なし

【bin\ をそのままコピーして別の場所で動かす】
   グローバル フォルダが無い → 見つからない

このため、

状況 対処
通常の配置 dotnet publish を使う(すべて揃う)
クラス ライブラリを他所に渡す CopyLocalLockFileAssemblies
プラグイン DLL を作る CopyLocalLockFileAssemblies + EnableDynamicLoading

**原則は「dotnet publish を使う」**であり、
CopyLocalLockFileAssemblies
ビルド出力を直接配る必要がある場合の回避策と位置づけられる
.NET Coreのデプロイ)。

NU1701 は、
「.NET Framework 用のパッケージを .NET Core 系から参照した」
という警告である。動くこともあるが保証されないため、

  1. netstandard2.0 対応版があるか探す(第一候補)
  2. 無ければ代替ライブラリを探す
  3. どうしても必要なら NoWarn で抑止し、動作確認を厚くする

という順で検討する。

参考サイト

nuget.org

現在登録されている NuGet パッケージを検索可能

NuGet の機能や使い方などのドキュメントを閲覧可能

Microsoft Learn

ツール

PowerShell

移行


Tags: 移行, .NET開発, デプロイ, NuGet

NetDevInfraWiki

マイクロソフト系技術情報 Wiki
Open 棟梁 Wiki

(未着手)

開発基盤部会 Wiki

移行管理: DONETODO

Clone this wiki locally