Generated by Perplexity
問題說明
前陣子我遇到一個值得記錄的問題(至少對我而言):在維護一個有點舊的 .NET framework 專案時,發現透過 ProjectReference 方式依賴的 NuGet 套件無法自動還原。經過一番調查後,才發現這是 NuGet 在處理某些舊式專案時的一個行為特性。對 .NET 很不熟的我覺得可以記錄一下,以防未來又忘記。
專案結構與重現情境
MainApp.csproj ← 主專案,透過 ProjectReference 參考 SharedLibrary.csproj
└── SharedLibrary.csproj
└── <PackageReference Include="ICSharpCode.SharpZipLib" />
我的 solution 中有兩個專案:
- MainApp.csproj:主應用程式專案
- SharedLibrary.csproj:共用函式庫,透過 ProjectReference 被 MainApp 參考
在 SharedLibrary.csproj 中,使用了常見的壓縮解壓縮函式庫 ICSharpCode.SharpZipLib,並以以下方式引用:
<PackageReference Include="ICSharpCode.SharpZipLib" Version="1.4.2" />
然而在建置 MainApp.csproj 時,卻發現 SharpZipLib 的相關類別無法解析,沒有編譯出 .dll 檔。反觀其他套件如 NPOI 和 EntityFramework 則一切正常,讓我相當困惑(因為不論是在 prodution 或是 test 環境中都沒看到 .dll 缺失的問題)。
至於為什麼會發生這種事情,我想…就技術債這三個字吧
嘗試與排查過程
一開始我找問題的方式比較粗暴簡單,那就是找其他也有引用 SharedLibrary.csproj,但卻可以成功編譯的專案,看他們跟有問題的專案差在哪裡。於是我找到了其他專案多的這一行:
<PackageReference Include="EntityFramework" Version="5.0.0" />
神奇的事情發生了,SharpZipLib 居然也能正確還原了! 這讓我開始懷疑問題出在 NuGet 的套件還原邏輯上。經過研究,終於找到癥結所在。
NuGet 的套件還原邏輯
NuGet 在處理 舊式 .NET Framework 專案(沒有任何 PackageReference 的 .csproj) 時,如果主專案沒有明確引入任何套件,則在進行還原時不會遞迴還原其 ProjectReference 的傳遞性依賴。
換句話說:
- 如果 MainApp.csproj 沒有任何
<PackageReference>
,NuGet restore 不會主動還原 SharedLibrary 中的 SharpZipLib。 - 一旦在 MainApp.csproj 中加入任一
<PackageReference>
,NuGet 便會啟用完整的還原機制,連帶還原所有傳遞性套件。
這也解釋了為何某些套件可以正常還原,而有些(如 SharpZipLib)卻沒有編譯出來。
解法與建議
針對這個問題,有幾種可行的解法:
方法一:在主專案加入任一 PackageReference(workaround)
即使是無關緊要的套件,只要出現一個 <PackageReference>
,就能觸發 NuGet 啟用遞迴還原邏輯。
但這是最直觀的 workaround,因此不建議。
方法二:設定 RestoreProjectStyle 為 PackageReference
在 MainApp.csproj 加入以下設定:
<PropertyGroup>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup>
這樣做能強制 NuGet 以 PackageReference 模式來還原,不需添加額外套件,語意也更清楚。
方法三:改回使用 packages.config
你也可以選擇將 NuGet 管理方式統一改為 .NET framework 預設的 packages.config,但在我維護的專案中已經都用 PackageReference 了,我就不採取這個做法了。
後記
手上 .NET 專案大部分都是維護,偶爾有些小需求,而這次是要將專案都走 Azure DevOps,所以才會發現這些問題。如果有理解錯誤的地方,也請用力地指正。
Ref: