NuGet 如何解析套件相依性
只要安裝或重新安裝套件 (包含安裝為還原程序一部分的套件),NuGet 也會安裝與這個第一個套件相依的任何其他套件。
這些立即相依性接著會有其自己的相依性,而這會繼續到任意深度。 這會產生所謂的「相依性圖形」,以描述所有層級上套件之間的關聯性。
多個套件具有相同的相依性時,同一個套件識別碼可能會多次出現在圖形中,但可能具有不同版本的條件約束。 不過,專案中只能使用一個指定套件的版本,因此 NuGet 必須選擇要使用的版本。 確切的處理序取決於所使用的套件管理格式。
使用 PackageReference 的相依性解析
使用 PackageReference 格式來將套件安裝至專案時,NuGet 會新增適當檔案中一般套件圖形的參考,並事先解決衝突。 此程序稱為「可轉移還原」。 重新安裝或還原套件則是下載圖形中所列套件的程序,導致更快速且更容易預測的組建。
您也可以利用浮動版本,例如 2.8.*,以避免修改專案以使用最新版本的套件。 使用浮動版本時,建議您啟用 鎖定檔案功能 ,以確保可重複性。
若 NuGet 還原程序在建置之前執行,就會先解析記憶體中的相依性,然後將產生的圖形寫入稱為 project.assets.json
的檔案。
資產檔案位於 MSBuildProjectExtensionsPath
,其預設為專案的 'obj' 資料夾。
MSBuild 接著會讀取這個檔案,並將它轉譯成一組可找到可能參考的資料夾,然後將它們新增至記憶體中的專案樹狀結構。
project.assets.json
檔案是暫時的,不應該新增至原始程式碼控制。 它預設會列在 .gitignore
和 .tfignore
中。 請參閱套件和原始檔控制。
相依性解析規則
可轉移還原會套用四個主要規則來解決相依性:最低適用的版本、浮動版本、直接相依性與表親相依性。
最低適用版本
最低適用版本規則可還原依其相依性所定義之最低可能版本的套件。 除非宣告為浮動,否則它也適用於與應用程式或類別庫的相依性。
例如,在下圖中,將 1.0-beta 視為低於 1.0,因此 NuGet 會選擇 1.0 版:
在下一個圖中,摘要上無法使用 2.1 版,但因為版本條件約束是 >= 2.1 NuGet 會挑選下一個可以找到的最低版本,在此案例中為 2.2:
如果應用程式指定不適用於摘要的確切版本號碼 (例如 1.2),則嘗試安裝或還原套件時,NuGet 會失敗並發生錯誤:
浮動版本
使用 * 字元指定浮動相依性版本。 例如: 6.0.*
。 此版本規格指出「使用最新的 6.0.x 版」; 4.*
表示「使用最新的 4.x 版本」。使用浮動版本可減少專案檔的變更,同時保持最新版的相依性。
浮動版本只能在專案層級指定。
使用浮動版本時,NuGet 會解析符合版本模式的最高套件版本,例如 6.0.*
取得以 6.0 開頭的最高套件版本:
版本 | 伺服器上存在的版本 | 解決方法 | 原因 | 備註 |
---|---|---|---|---|
* | 1.1.0 1.1.1 1.2.0 1.3.0-alpha |
1.2.0 | 最高的穩定版本。 | |
1.1.* | 1.1.0 1.1.1 1.1.2-alpha 1.2.0-alpha |
1.1.1 | 符合指定模式的最高穩定版本。 | |
*-* | 1.1.0 1.1.1 1.1.2-alpha 1.3.0-beta |
1.3.0-beta | 最高版本,包括不穩定的版本。 | 可在 Visual Studio 16.6 版、NuGet 5.6 版、.NET Core SDK 3.1.300 版中使用 |
1.1.*-* | 1.1.0 1.1.1 1.1.2-alpha 1.1.2-beta 1.3.0-beta |
1.1.2-beta | 與模式相關的最高版本,包括不穩定的版本。 | 可在 Visual Studio 16.6 版、NuGet 5.6 版、.NET Core SDK 3.1.300 版中使用 |
注意
浮動版本解析不會考慮是否列出套件。 如果條件可以符合全域封裝資料夾中的套件,則會在本機解析浮動版本解析。
直接相依性獲勝
當應用程式的套件圖形包含相同子檔中不同版本的套件時,而其中一個版本是該子檔中的直接相依性,則會針對該子圖形選擇該版本,而其餘版本則會忽略。 此行為允許應用程式覆寫相依性圖形中的任何特定套件版本。
在下列範例中,應用程式直接相依於套件 B,版本條件約束為 >=2.0.0。 應用程式也相依於套件 A,而封裝 A 則依存於套件 B,但具有 >=1.0.0 條件約束。 因為套件 B 2.0.0 上的相依性是圖形中應用程式的直接相依性,因此會使用該版本:
警告
直接相依性獲勝規則可能會導致套件版本的降級,因此可能會中斷圖形中的其他相依性。 降級套件時,NuGet 會新增 警告來警示使用者。
此規則也會導致大型相依性圖表的效率更高。 當相同子檔中的相依性比進一步的相依性更高時,NuGet 會忽略該相依性,而 NuGet 也會忽略圖形該分支上所有剩餘的相依性。
例如,在下圖中,因為使用 Package C 2.0.0,NuGet 會忽略該子文件中參考舊版 Package C 的任何分支:
透過此規則,NuGet 會嘗試接受套件作者的意圖。 在下圖中,套件 A 的作者已從 Package C 2.0.0 明確降級為 Package C 1.0.0。
應用程式擁有者可以選擇將套件 C 升級至高於 2.0.0 的版本,因此不會進一步降級套件 C 的版本。在此情況下,不會引發任何警告。
鄰近相依性
當圖形中的不同子文件中參考不同的套件版本時,NuGet 會使用符合所有版本需求的最低版本(如同 最低適用的版本 和 浮動版本 規則)。 例如,在下圖中,套件 B 2.0.0 版滿足另一個 >=1.0.0 條件約束,因此會使用:
請注意,套件不需要位於要套用之表親相依性規則的相同距離。 在下圖中,Package D 2.0.0 是在 Package C 子文件中選擇,而 Package D D 3.0.0 則是在套件 A 的子文件中選擇。在 [應用程式] 子檔中,沒有套件 D 的直接相依性,因此 會套用最低適用的版本 規則,並選擇 3.0.0 版。
在某些情況下,無法符合所有版本需求。 如下所示,如果套件 A 需要套件 B 1.0.0,而套件 C 需要套件 B >=2.0.0,則 NuGet 無法解析相依性併產生錯誤。
在這些情況下,最上層取用者(應用程式或套件)應該在套件 B 上新增自己的直接相依性, 以便套用直接相依性取用 規則。
PackageReference 的版本範圍和發行前版本
套件提供穩定和發行前版本並不罕見。
解析相依性圖形時,NuGet 會決定是否要根據單一規則考慮套件的發行前版本: If the project or any packages within the graph request a prerelease version of a package, then include both prerelease or stable versions, otherwise consider stable versions only.
實際上,在最低適用的規則下,這表示:
版本範圍 | 可用版本 | 已選取的版本 |
---|---|---|
[1.0.0, 2.0.0) | 1.2.0-beta.1、1.2.0、 | 1.2.0 |
[1.0.0, 2.0.0-0) | 1.2.0-beta.1、1.2.0、 | 1.2.0-beta.1 |
[1.0.0, 2.0.0) | 1.2.0-beta.1、2.0.0-beta.3 | 無, 會引發 NU1103 。 |
[1.0.0, 2.0.0-rc) | 1.2.0-beta.1、2.0.0-beta.3 | 1.2.0-beta.1 |
使用 packages.config 的相依性解析
使用 packages.config
,專案的相依性會寫入至 packages.config
作為一般清單。 這些套件的任何相依性也會寫入相同的清單中。 安裝套件之後,NuGet 也可能會修改 .csproj
檔案、app.config
、web.config
和其他個別檔案。
使用 packages.config
,NuGet 會嘗試在每個個別套件安裝期間解決相依性衝突。 也就是說,如果套件 A 已安裝並與套件 B 相依,而且套件 B 已列在 packages.config
中作為其他項目的相依性,則 NuGet 會比較所要求的套件 B 版本,並嘗試找到符合所有版本條件約束的版本。 具體來說,NuGet 會選取符合相依性的較低 major.minor 版本。
根據預設,NuGet 2.8 會尋找最低的修補程式版本 (請參閱 NuGet 2.8 版本資訊)。 您可以透過 NuGet.Config
中的 DependencyVersion
屬性和命令列上的 -DependencyVersion
參數,來控制此設定。
針對較大的相依性圖形,解析相依性的 packages.config
程序會更為複雜。 每個新套件安裝都需要周遊整個圖形,而且會引發版本衝突機會。 發生衝突時,會停止安裝,並讓專案處於不定狀態,特別是對專案檔本身進行可能的修改。 使用其他套件管理格式時,這不是問題。
版本範圍和發行前版本與 packages.config
packages.config 解析不允許在圖表中混合穩定和發行前版本相依性。
如果相依性以類似 [1.0.0, 2.0.0)
的範圍來表示,圖表中不允許發行前版本套件。
管理相依性資產
使用 PackageReference 格式時,您可以控制從相依性流入最上層專案的資產。 如需詳細資訊,請參閱 PackageReference。
最上層專案本身是套件時,也可以搭配使用 include
和 exclude
屬性與 .nuspec
檔案中所列相依性來控制此流量。 請參閱 .nuspec 參考 - 相依性。
排除參考
在某些情況下,可能會在專案中多次參考同名的組件,並產生設計階段和建置時間錯誤。 請考慮包含 C.dll
之自訂版本的專案,並參考也包含 C.dll
的套件 C。 同時,專案也與套件 B 相依,而套件 B 也與套件 C 和 C.dll
相依。 因此,NuGet 無法決定要使用的 C.dll
,但您不能只移除專案與套件 C 的相依性,因為套件 B 也與其相依。
若要解決此問題,您必須直接參考想要的 C.dll
(或使用另一個參考正確項目的套件),然後新增與排除所有資產之套件 C 的相依性。 這是根據使用中的套件管理格式所進行,如下所示:
PackageReference:在相依性中新增
ExcludeAssets="All"
:<PackageReference Include="PackageC" Version="1.0.0" ExcludeAssets="All" />
packages.config
:從.csproj
檔案中移除套件 C 的參考,讓它只參考您想要的C.dll
版本。
套件安裝期間的相依性更新
如果已符合相依性版本,則不會在其他套件安裝期間更新相依性。 例如,請考慮使用與套件 B 相依並指定 1.0 作為版本號碼的套件 A。 來源存放庫包含 1.0、1.1 和 1.2 版的套件 B。如果在已包含 B 1.0 版的專案中安裝 A,則 B 1.0 仍會維持使用中狀態,因為它符合版本限制。 不過,如果套件 A 要求 1.1 版或更高版本的 B,則會安裝 B 1.2。
解決不相容的套件錯誤
在套件還原作業期間,您可能會看到「一或多個套件不相容...」錯誤,或套件與專案目標架構「不相容」。
專案中參考的一或多個套件未指出它們支援專案的目標架構時,會發生此錯誤;也就是說,套件在其 lib
資料夾中未包含與專案相容之目標架構的適合 DLL (如需清單,請參閱目標架構)。
例如,如果專案的目標設為 netstandard1.6
,而且您嘗試安裝只包含 lib\net20
和 \lib\net45
資料夾中 DLL 的套件,則會看到套件和其相依項的下列這類訊息:
Restoring packages for myproject.csproj...
Package ContosoUtilities 2.1.2.3 is not compatible with netstandard1.6 (.NETStandard,Version=v1.6). Package ContosoUtilities 2.1.2.3 supports:
- net20 (.NETFramework,Version=v2.0)
- net45 (.NETFramework,Version=v4.5)
Package ContosoCore 0.86.0 is not compatible with netstandard1.6 (.NETStandard,Version=v1.6). Package ContosoCore 0.86.0 supports:
- 11 (11,Version=v0.0)
- net20 (.NETFramework,Version=v2.0)
- sl3 (Silverlight,Version=v3.0)
- sl4 (Silverlight,Version=v4.0)
One or more packages are incompatible with .NETStandard,Version=v1.6.
Package restore failed. Rolling back package changes for 'MyProject'.
若要解決不相容,請執行下列其中一項:
- 將專案的目標重新設為您想要使用的套件所支援的架構。
- 請連絡套件作者,並與他們合作以新增所選擇架構的支援。 基於此用途,nuget.org 上的每個套件列出頁面都具有連絡人負責人連結。
提示
替代解決方案:NuGetSolver 是由 Microsoft DevLabs 開發的 Visual Studio 延伸模組,其設計目的是協助解決相依性衝突。 它會自動化識別和解決這些問題的程式。 如需進一步的詳細數據,請流覽 Visual Studio Marketplace上的 NuGetSolver 頁面,我們很樂意聽到您對體驗的意見反應。