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

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

![](https://stwillblogassets.blob.core.windows.net/files/images/external/user-images.githubusercontent.com/86d0e51bd2d7ac15d8ea-197664693-65a8c74e-af07-4f33-ae6c-2704bac8d467.png)

### 時代的眼淚 ( .NET Standard )

早在 2015 年 .NET Core 1.0 問世的時候，微軟宣稱他們提出 [.NET Standard](https://learn.microsoft.com/zh-tw/dotnet/standard/net-standard?tabs=net-standard-2-0&WT.mc_id=DT-MVP-4015686) 的概念，可以幫助你開發出跨目標框架(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](https://www.nuget.org/packages/Meilisearch#dependencies-body-tab) 套件，我們加入專案後，立刻出現無法編譯專案的狀況，如下圖示：

![System.Runtime.CompilerServices.Unsafe doesn't support netcoreapp2.1. Consider updating your TargetFramework to netcoreapp3.1 or later.](https://stwillblogassets.blob.core.windows.net/files/images/external/user-images.githubusercontent.com/d258b3171ac88c51963d-197551261-88834677-da8b-4275-b9a3-f50ae089c36b.png)

其實錯誤訊息很清楚的提到 [System.Runtime.CompilerServices.Unsafe](https://www.nuget.org/packages/System.Runtime.CompilerServices.Unsafe) 不支援 `netcoreapp2.1` 這個 **TFM** (目標框架名稱)，還說叫我考慮升級到 `netcoreapp3.1` 版本！（如果可以我也想）

我們從官網的 [.NET Standard](https://learn.microsoft.com/zh-tw/dotnet/standard/net-standard?tabs=net-standard-2-0&WT.mc_id=DT-MVP-4015686) 文件可以看到，其實 .NET Standard 2.0 已經寫了明確支援 .NET Core 2.0 後的所有版本，怎麼這個套件就不能用了呢？

![image](https://stwillblogassets.blob.core.windows.net/files/images/external/user-images.githubusercontent.com/3c91d16dbf77692fc9dc-197551638-c631c0cc-22a7-46a4-97cf-4acbfe2d6e8b.png)

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

為了追查原因，我花了一些時間追蹤 [MeiliSearch](https://www.nuget.org/packages/Meilisearch#dependencies-body-tab) 套件不能使用的原因，以下是追查步驟：

1.  查看 [MeiliSearch 0.12.0](https://www.nuget.org/packages/Meilisearch#dependencies-body-tab) 的 `Dependencies` 資訊
    
    ![NuGet Gallery | MeiliSearch 0.12.0](https://stwillblogassets.blob.core.windows.net/files/images/external/user-images.githubusercontent.com/f7e1c0e5b18f6ac50aee-197553040-c02f17c8-d595-4d6c-9b4a-bc818a04c901.png)
    
2.  接著查 [JWT 9.0.3](https://www.nuget.org/packages/JWT/) 的 `Dependencies` 資訊
    
    這裡我從 `.NETStandard 2.0` 的相依套件清單中發現好幾個相依套件，基本上每個都要再點進去看，才知道是不是有任何一個套件用到 [System.Runtime.CompilerServices.Unsafe](https://www.nuget.org/packages/System.Runtime.CompilerServices.Unsafe) 這個套件。
    
    ![image](https://stwillblogassets.blob.core.windows.net/files/images/external/user-images.githubusercontent.com/0fb2b767e1f426970e83-197555647-0dafe832-f30a-49b5-bd0d-26ac78b61289.png)
    
    結果追查下去，通通沒有！
    
3.  接著查 [System.Net.Http.Json 6.0.0](https://www.nuget.org/packages/System.Net.Http.Json/) 的 `Dependencies` 資訊
    
    這個套件也有好幾個相依套件，我也是一個一個追查下去：
    
    ![image](https://stwillblogassets.blob.core.windows.net/files/images/external/user-images.githubusercontent.com/61fbae15b0f4fea9ce45-197576863-f0165876-d96e-4361-9211-3553c6e1999c.png)
    
    最後查到在 [System.Memory](https://www.nuget.org/packages/System.Memory/) 與 [System.Text.Json](https://www.nuget.org/packages/System.Text.Json/) 套件中的 `.NETStandard 2.0` TFM 都有用到 [System.Runtime.CompilerServices.Unsafe](https://www.nuget.org/packages/System.Runtime.CompilerServices.Unsafe) 這個套件！
    
4.  最後追查 [System.Runtime.CompilerServices.Unsafe](https://www.nuget.org/packages/System.Runtime.CompilerServices.Unsafe) 的 `Dependencies` 資訊
    
    這個微軟的套件很明確的提到他在 `.NETStandard 2.0` 這個目標下並沒有相依套件：
    
    ![image](https://stwillblogassets.blob.core.windows.net/files/images/external/user-images.githubusercontent.com/25a1d3d12abb2cf9790c-197577981-9b149836-7fa3-47a2-bdbd-57bdda7acba3.png)
    
    相依清單中，確實沒有列出 `.NETCoreApp 2.1` 了，因為基於 [NETStandard Compatibility Error infrastructure](https://github.com/dotnet/runtime/blob/main/docs/coding-guidelines/libraries-packaging.md#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 套件的[原始碼](https://github.com/meilisearch/meilisearch-dotnet)回來修改
    
    我將該原始碼專案中的 [System.Net.Http.Json 6.0.0](https://www.nuget.org/packages/System.Net.Http.Json/) 相依套件降到 `5.0.0` 版本，這個版本就會用到舊的 [System.Runtime.CompilerServices.Unsafe 5.0.0](https://www.nuget.org/packages/System.Runtime.CompilerServices.Unsafe/5.0.0) 版本，此版本就可以順利跟 .NETCoreApp 2.1 的專案一起建置了！
    
    > 感謝這個 Open Source 滿天飛的世界！😊
    
2.  在專案的 `*.csproj` 專案檔中設定一個 `SuppressTfmSupportBuildWarnings` Property (屬性)，讓 MSBuild 在建置專案時，直接忽略 TFM 不相容的錯誤！
    
    設定範例如下：
    
    ```xml
    <PropertyGroup>
      <OutputType>Exe</OutputType>
      <TargetFramework>netcoreapp2.1</TargetFramework>
      <!-- 👇 加入這一行 👇 -->
      <SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
    </PropertyGroup>
    ```
    
    ![image](https://stwillblogassets.blob.core.windows.net/files/images/external/user-images.githubusercontent.com/b9382e4ea40b4266e80a-197581112-fd7f392d-7c85-40b2-9bf6-9b29eeb4f8a5.png)
    
    加上這段屬性後，建置時就不會再出現錯誤訊息，我們的專案也可以順利被編譯了！👍
    
    > 這就是我想要的解答，用下去風險自負我可以接受，但不要讓我連建置都不行！
    

### 總結

這次的經驗告訴我，不要相信什麼天長地久，原本 .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 的過！👍

### 相關連結

-   [Please stop lying about .NET Standard 2.0 support!](https://andrewlock.net/stop-lying-about-netstandard-2-support/)
-   [.NET Core 2.1 will reach End of Support on August 21, 2021](https://devblogs.microsoft.com/dotnet/net-core-2-1-will-reach-end-of-support-on-august-21-2021/?WT.mc_id=DT-MVP-4015686)
-   [NETStandard Compatibility Error infrastructure](https://github.com/dotnet/runtime/blob/main/docs/coding-guidelines/libraries-packaging.md#netstandard-compatibility-error-infrastructure)
-   [System.Runtime.CompilerServices.Unsafe is not obey .NET Standard 2.0 (it should support .NET Core 2.1 App) #77367](https://github.com/dotnet/runtime/issues/77367)
-   [.NET Standard | Microsoft Learn](https://learn.microsoft.com/zh-tw/dotnet/standard/net-standard?WT.mc_id=DT-MVP-4015686&tabs=net-standard-2-0)
