The Will Will Web

記載著 Will 在網路世界的學習心得與技術分享

如何在舊版的 .NET Core 專案使用不支援當前目標框架的 NuGet 套件

我們最近有個維護案,其技術架構是採用已經 EOS (產品結束支援) 的 .NET Core 2.1 版本,由於客戶沒有預算可以升級框架版本,所以只能持續在現有的不受支援的 .NET Core 版本上新增功能。因為本次專案需求正好要用到一個支援 .NET Standard 2.0 的 MeiliSearch 套件,理論上應該支援 .NET Core 2.1 的,但卻遇到了無法編譯的狀況。本篇文章我就來說說這種狀況的解決方法。

時代的眼淚 ( .NET Standard )

早在 2015 年 .NET Core 1.0 問世的時候,微軟宣稱他們提出 .NET Standard 的概念,可以幫助你開發出跨目標框架(TFM)的 .NET 函式庫,編譯出來的函式庫可以同時兼容於 .NET Framework 與 .NET Core 之間。不過,自從 .NET 5 出現之後,微軟就將 .NET Standard 廢棄了,還說 .NET Standard 2.1 就是最後一個 .NET Standard 版本,這個版本不支援 .NET Framework,而且僅支援 .NET Core 3.0 以上版本,且以後也不會再強調 .NET Standard 的存在了。

事實上,打從第一版開始的 .NET Standard 就承諾,早期開發出來的 .NET Standard 標準函式庫絕對可以相容於未來的所有 .NET 框架版本。嗯哼~

追查問題的根本原因

我原本認為這個承諾會一直保持下去,直到昨天膝蓋中了一箭!🦶

我們有個 ASP.NET Core 2.1 的專案,因為需要用到一個號稱支援 .NET Standard 2.0 的 MeiliSearch 套件,我們加入專案後,立刻出現無法編譯專案的狀況,如下圖示:

System.Runtime.CompilerServices.Unsafe doesn't support netcoreapp2.1. Consider updating your TargetFramework to netcoreapp3.1 or later.

其實錯誤訊息很清楚的提到 System.Runtime.CompilerServices.Unsafe 不支援 netcoreapp2.1 這個 TFM (目標框架名稱),還說叫我考慮升級到 netcoreapp3.1 版本!(如果可以我也想)

我們從官網的 .NET Standard 文件可以看到,其實 .NET Standard 2.0 已經寫了明確支援 .NET Core 2.0 後的所有版本,怎麼這個套件就不能用了呢?

image

我之前在我開設的 ASP.NET Core 6.0 開發實戰課程中就有說過,其實 NuGet Gallery 上面的套件雖然都會標示 Dependencies (相依性) 資訊,但這些資訊都是「作者」自己標示的,不見得 100% 正確,因此在下載套件時,建議下載知名度高的套件,這樣可以避免一些不必要的問題。

為了追查原因,我花了一些時間追蹤 MeiliSearch 套件不能使用的原因,以下是追查步驟:

  1. 查看 MeiliSearch 0.12.0Dependencies 資訊

    NuGet Gallery | MeiliSearch 0.12.0

  2. 接著查 JWT 9.0.3Dependencies 資訊

    這裡我從 .NETStandard 2.0 的相依套件清單中發現好幾個相依套件,基本上每個都要再點進去看,才知道是不是有任何一個套件用到 System.Runtime.CompilerServices.Unsafe 這個套件。

    image

    結果追查下去,通通沒有!

  3. 接著查 System.Net.Http.Json 6.0.0Dependencies 資訊

    這個套件也有好幾個相依套件,我也是一個一個追查下去:

    image

    最後查到在 System.MemorySystem.Text.Json 套件中的 .NETStandard 2.0 TFM 都有用到 System.Runtime.CompilerServices.Unsafe 這個套件!

  4. 最後追查 System.Runtime.CompilerServices.UnsafeDependencies 資訊

    這個微軟的套件很明確的提到他在 .NETStandard 2.0 這個目標下並沒有相依套件:

    image

    相依清單中,確實沒有列出 .NETCoreApp 2.1 了,因為基於 NETStandard Compatibility Error infrastructure 的規定,他讓函式庫的作者可以讓特定套件版本明確取消支持微軟 EOL 的產品版本,所以微軟在 .NET Core 2.1 進入 EOL 狀態時 (August 21, 2021),之後所發佈的 NuGet 套件就直接不支援 .NET Core 2.1 了!

    我個人非常不能理解為什麼微軟硬要讓新版 NuGet 套件刻意不相容舊版的 .NET Core SDK,明明都都還可以用,就算不能用,也應該要讓我可以 Build 專案,出問題我可以自行負責!🔥

解決方案

由於專案連建置(Build)都無法進行,套件是完全無法使用的,但我又一定要用這個套件怎麼辦呢?我最終找出了兩個解決方案:

  1. 直接取得 Meilisearch .NET 套件的原始碼回來修改

    我將該原始碼專案中的 System.Net.Http.Json 6.0.0 相依套件降到 5.0.0 版本,這個版本就會用到舊的 System.Runtime.CompilerServices.Unsafe 5.0.0 版本,此版本就可以順利跟 .NETCoreApp 2.1 的專案一起建置了!

    感謝這個 Open Source 滿天飛的世界!😊

  2. 在專案的 *.csproj 專案檔中設定一個 SuppressTfmSupportBuildWarnings Property (屬性),讓 MSBuild 在建置專案時,直接忽略 TFM 不相容的錯誤!

    設定範例如下:

    <PropertyGroup>
      <OutputType>Exe</OutputType>
      <TargetFramework>netcoreapp2.1</TargetFramework>
      <!-- 👇 加入這一行 👇 -->
      <SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
    </PropertyGroup>
    

    image

    加上這段屬性後,建置時就不會再出現錯誤訊息,我們的專案也可以順利被編譯了!👍

    這就是我想要的解答,用下去風險自負我可以接受,但不要讓我連建置都不行!

總結

這次的經驗告訴我,不要相信什麼天長地久,原本 .NET Standard 這個美好的願景,短短 6 年就結束了。😒

我也再次驗證 NuGet 上面標示的 Dependencies (相依性) 資訊,真的只是參考用的,不一定準確,尤其是 .NET Standard 這個部分。

老實說,我所看過的開源軟體,只要背後有商業公司支援,品質通常有一定程度保障,也有好的支援服務,但就是有一定年限,沒有公司會陪著你走到天涯海角。你公司如果有舊版的 ASP.NET Core 專案,建議還是趕快升級吧。而且,你知道嗎?話說 .NET Core 3.1 將在今年的 December 13, 2022 正式退役,微軟在這天之後發佈的 NuGet 套件,預設將無法再使用於 .NET Core 3.1 專案囉!🔥

今後除非你看過我這篇文章,否則你遇到相同的時候,肯定會鬼打牆一段時間。我們這次遇到的問題,是公司同事遇到的,他鬼打牆了好幾天,根本不知道如何解決這種狀況。事實上,我相信有更多的 .NET 開發者是完全沒有能力解決此類問題的,因為過渡依賴開發工具的關係,不瞭解 NuGet 底層原理很難消化這類相依問題,而且會有滿滿的無力感,我完全可以感同身受。即便我認為我已經對 .NET 架構相當熟悉,要第一時間理解這個問題的脈絡,也是花了一個小時左右才找到解決方案。

如果是 GPL 或那種沒有商業公司在背後支援的開源軟體,你可以說大多數專案發展緩慢,也可以說支援度不太穩定,背後都靠社群志工在燃燒熱情。你不一定找的到人幫你,但至少知名的大型開源專案,都非常強調舊版的相容性,有些相容性可以多達十幾二十年之久,功能可以少一點,但原本的功能絕對不會壞掉!

總之,沒有銀子彈,有一好就沒兩好,人在江湖闖,哪能不挨刀,遇到了就認命點,別讓自己不開心!😅

這次經驗至少讓我學到了有個 <SuppressTfmSupportBuildWarnings> 屬性可以設定,NuGet 套件即便 TFM 不相容,專案依然可以 Build 的過!👍

相關連結