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 版中使用 |
1.2.0-rc.* | 1.1.0 1.2.0-rc.1 1.2.0-rc.2 1.2.0 |
1.2.0 | 儘管這是一個有預發行部分的版本範圍,但如果穩定版本符合穩定部分,則允許使用。 假設 1.2.0 > 1.2.0-rc.2,則會選擇它。 |
注意事項
浮動版本解析不會考慮套件是否已被列出。 如果條件可以符合全域封裝資料夾中的套件,則浮動版本解析會在本機完成。
直接相依性獲勝
當應用程式的套件圖形包含相同子檔中不同版本的套件時,而其中一個版本是該子檔中的直接相依性,則會針對該子圖形選擇該版本,而其餘版本則會忽略。 此行為可讓應用程式覆寫相依性圖形中的任何特定套件版本。
在下列範例中,應用程式直接相依於套件 B,版本條件約束為 >=2.0.0。 應用程式也相依於套件 A,而封裝 B 也會相依於套件 B,但具有 >=1.0.0 條件約束。 因為對套件 B 2.0.0 的相依性是圖形中應用程式的直接相依性,因此會使用該版本:
警告
直接相依性獲勝規則可能會導致套件版本的降級,因此可能會中斷圖形中的其他相依性。 降級套件時,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,此套件也相依於套件 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
檔案中移除 PackageC 的參考,使其只參考您想要的C.dll
版本。
套件安裝期間的相依性更新
如果相依性版本已經滿足,則相依性不會在其他套件安裝期間更新。 例如,請考慮相依於套件 B 的套件 A,並針對版本號碼指定 1.0。 來源存放庫包含 1.0、1.1 和 1.2 版的套件 B。如果 A 已安裝在已經包含 B 1.0 版的專案中,B 1.0 會繼續使用,因為它符合版本條件約束。 不過,如果套件 A 需要 B 的 1.1 版或更高版本,則會安裝 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 頁面,我們很樂意聽到您對體驗的意見反應。