組件在建置期間會以兩種不同的方式使用。 第一個是用於編譯,它允許套件取用者的程式碼針對元件中的 API 進行編譯,以及 Intellisense 提供建議。 第二個是運行時,將程序集複製到目錄中 bin ,並在程式執行期間使用。 某些套件作者只想要在編譯階段使用自己的元件 (或元件的子集) 供其套件取用者使用,但需要提供執行階段的所有相依性。 本文件著眼於實現此結果的方法。
建議:每個包裝一個組件
我們的建議是每個組件都要有一個套件,並且套件之間應包括與其他組件的相依性。 當 NuGet 還原專案時,它會執行資產選取,並支援包含、排除和建立私人不同的資產類別。 為了防止您的套件的相依項目成為使用者在編譯時的資產,您可以將 compile 資產設為私有的。 在產生的套件中,這將導致 compile 從相依性中排除。 請注意,當未提供任何資產時,預設專用資產是 contentfiles;build;analyzers。 因此,您應該在 PrivateAssets="compile;contentfiles;build;analyzers" 或 PackageReference中使用 ProjectReference 。
<ItemGroup>
<ProjectReference Include="..\OtherProject\OtherProject.csproj" PrivateAssets="compile;contentfiles;build;analyzers" />
<PackageReference Include="SomePackage" Version="1.2.3" PrivateAssets="compile;contentfiles;build;analyzers" />
</ItemGroup>
如果您要從自訂 nuspec 檔案建立套件,而不是讓 NuGet 自動為您產生套件, nuspec 您應該使用 exclude XML 屬性。
<dependencies>
<group targetFramework=".NETFramework4.8">
<dependency id="OtherProject" version="3.2.1" exclude="Compile,Build,Analyzers" />
<dependency id="SomePackage" version="1.2.3" exclude="Compile,Build,Analyzers" />
</group>
</dependencies>
這是推薦的解決方案有三個原因。
首先,有用的程序集通常會被新的程序集/包引用。 雖然公用程式元件目前可能只供單一套件使用,因此很誘人在單一套件中提供這兩個元件,但如果第二個套件想要在未來使用「私人」公用程式元件,則必須將公用程式元件移至新的套件中,而且需要更新舊套件以將其宣告為相依性, 或者公用程式套件需要同時出貨現有和新套件。 如果元件隨附於兩個不同的套件中,且專案參考這兩個套件,如果兩個套件中有不同版本的公用程式元件,NuGet 將無法協助版本管理。
其次,有時使用您的套件的開發人員可能也想使用依賴項中的 API。 例如,請考慮套件 Microsoft.ServiceHub.Client 3.0.3078 版。 如果您下載套件並檢查檔案 nuspec ,您可以看到它列出了兩個以開 Microsoft.VisualStudio. 頭的套件作為依賴項,這意味著它在運行時需要它們,但它也排除了它們的編譯資產。 這表示使用 Microsoft.ServiceHub.Client 的專案將不會在 IntelliSense 中提供 Visual Studio API,或建置專案,除非專案明確安裝這些套件。 這是具有排除資產的套件相依性所具有的優點。 使用您的套件的專案,如果他們也想使用您的依賴項,可以新增套件的參考,讓 API 對自己可用。
最後,一些套件作者過去曾對 NuGet 的元件選擇感到困惑,以選取支援多個目標架構的套件,當其套件也包含多個元件時。 如果您的主要元件支援公用程式元件的不同目標架構,則可能不太清楚要將所有元件放入哪些 lib/ 目錄。 藉由依元件名稱分隔每個套件,每個元件應該進入哪些 lib/ 資料夾會更直觀。 請注意,這並不意味著擁有 Package1.net48 和 Package1.net6.0 包裝。 它的意思是在 Package1 有 lib/net48/Package1.dll 和 lib/net6.0/Package6.0,在 Package2 有 lib/netstandard2.0/Package2.dll 和 lib/net5.0/Package2.dll。 當 Nuget 還原專案時,Nuget 會獨立執行兩個套件的資產選取。
請注意,依賴項的包含/排除資產僅適用於使用 PackageReference 的專案。 任何使用packages.config安裝您的套件的專案都會安裝您的相依套件,並使其 API 也可用。
packages.config 僅受 Visual Studio 較舊的 .NET Framework 專案範本支援。 SDK 樣式專案(即便是以 .NET Framework 為目標的專案),由於不支援 packages.config,因此它們具備管理相依性中包含/排除資產的功能。
不建議:一個封裝中的多個組件
PackageReference 並 packages.config 具有不同的可用功能。 無論您要支援您的套件消費者是使用PackageReference、packages.config,或同時使用這兩者,這都會影響到您撰寫套件的方式。
NuGet 的 MSBuild Pack 目標不支援在套件中自動包含專案參考。 它只會將那些引用的專案列為套件相依性。
GitHub 上有一個問題,社群成員會分享他們達成此結果的方式,這通常牽涉到使用 MSBuild 項目中繼資料PackagePath將檔案放在套件中的任何位置,如如何在套件中包含內容的文件中所述,並使用SuppressDependenciesWhenPacking來避免專案參考成為套件相依性。 還有社群開發的工具,可作為支援此功能的 NuGet 官方套件的替代方案。
PackageReference 支援
當套件取用者使用 PackageReference時,NuGet 會獨立選取編譯和執行階段資產,如先前所述。
偏好編譯資產 ref/<tfm>/*.dll(例如 ref/net6.0/*.dll),但如果該項不存在,則將回退到 lib/<tfm>/*.dll(例如 lib/net6.0/*.dll)。
執行階段資產偏好 runtimes/<rid>/lib/<tfm>/*.dll (例如 (runtimes/win11-x64/lib/net6.0/*.dll)),但如果不存在,則會回復至 lib/<tfm>/*.dll。
由於執行階段不會使用ref\<tfm>\的元件,它們可以是僅含中繼資料的元件,以減少套件大小。
packages.config 支援
使用 packages.config 來管理 NuGet 套件的專案通常會新增所有位於 lib\<tfm>\ 目錄中的元件參考。 目錄 ref\ 已新增至支援 PackageReference ,因此在使用 packages.config時不會考慮。 若要明確設定專案使用哪些元件作為參考,套件必須在 nuspec 檔案中使用
<references>
<group targetFramework="net45">
<reference file="MyLibrary.dll" />
</group>
</references>
MSBuild 套件目標不支援元素 <references> 。 請參考使用 MSBuild 套件時 使用 .nuspec 檔案打包的說明文件。
備註
packages.config專案會使用名為 bin\<configuration>\ 的程式,將元件複製到輸出目錄。 系統會複製專案的元件,然後建置系統會查看參考元件的元件資訊清單,然後複製這些元件,並針對所有元件遞迴重複。 這表示,如果任何元件僅透過反射(如 Assembly.Load、MEF 或其他相依性插入架構)載入,則即使位於 bin\<tfm>\,也可能不會被複製到專案的 bin\<configuration>\ 輸出目錄中。 這也表示這僅適用於 .NET 元件,不適用於使用 P/Invoke 呼叫的原生程式碼。
支援PackageReference和packages.config
這很重要
如果套件包含 <references> 元素的 nuspec 檔案,且在 ref\<tfm>\ 中不包含任何元件,NuGet 將會把在 nuspec <references> 元素中列出的元件公告為編譯及執行時期的資產。 這表示當參考的元件需要載入目錄中的 lib\<tfm>\ 任何其他元件時,將會有執行階段例外狀況。 因此,請務必同時使用 nuspec <references> 來提供 packages.config 支援,以及將元件複製到 ref/ 資料夾中以提供 PackageReference 支援。
runtimes/不需要使用套件資料夾,為了完整起見,已將其新增至上述部分。
Example
我的套件將包含三個元件 、 MyLib.dll和 MyHelpers.dllMyUtilities.dll,它們以 .NET Framework 4.7.2 為目標。
MyUtilities.dll 包含僅供其他兩個程序集使用的類,因此我不想在 IntelliSense 中或在編譯時將這些類提供給使用我的包的專案。 我的 nuspec 檔案需要包含以下XML元素:
<references>
<group targetFramework="net472">
<reference file="MyLib.dll" />
<reference file="MyHelpers.dll" />
</group>
</references>
我需要確保我的包裹內容是:
lib\net472\MyLib.dll
lib\net472\MyHelpers.dll
lib\net472\MyUtilities.dll
ref\net472\MyLib.dll
ref\net472\MyHelpers.dll