﻿# ASP.NET Core 的 Microsoft.AspNetCore.App 中繼套件載入問題與心得分享

我前陣遇到一個 ASP.NET Core 2.0 專案，在我安裝完 .NET Core 2.1 SDK 之後，卻發生網站無法正常運作的問題。經過一番研究之後，發現了一些有趣的現象，特此紀錄這段學習歷程。

### 重現問題

首先，要能重現這個問題，必須符合以下環境要求：

-   已安裝 .NET Core SDK 2.1.300 ( .NET Core Runtime 2.1.0 )
-   已安裝 .NET Core SDK 2.1.302 ( .NET Core Runtime 2.1.2 )

用以下命令建立 ASP.NET Core 2.1 的 MVC 專案

```
dotnet new mvc -n mvc20
```

使用 Visual Studio Code 開啟 `mvc20` 目錄。

建立 `global.json` 設定檔，並設定 SDK 版本為 `2.1.300` ( .NET Core 2.1.0 )，代表我想用舊版的 .NET Core SDK 執行 ASP.NET Core 應用程式。

```
dotnet new globaljson --sdk-version 2.1.300
```

除了建立 `global.json` 之外，也可以直接修改 `mvc20.csproj` 並指定 `Microsoft.AspNetCore.App` 的 `Version` 屬性為 `2.1.0`，這樣也可以重現問題。總之就是維持原有應用程式使用舊版的 NuGet 套件執行。

然後執行

```
dotnet run
```

執行的過程中，請手動修改 `Views/Home/Index.cshtml` 引發 View 自動重新編譯。此時到瀏覽器「重新整理」網頁，就會看到以下錯誤：

```
The type 'RazorViewAttribute' exists in both 'Microsoft.AspNetCore.Mvc.Razor, Version=2.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60' and 'Microsoft.AspNetCore.Mvc.Razor, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'
```

### 發生原因

從 .NET Core 2.0 開始，有個 **Roll forward** 機制，他會自動搜尋目前的 .NET Core Runtime 是否有新版本，如果有新的 minor (次版號) 版本，就會自動選用最新的版本來載入，如果有新的 patch (修訂號) 自然也是。也就是說，如果系統中安裝了 .NET Core 2.0 與 2.1 版本，預設會自動選用最新版本 2.1 來執行。

> 備註：依據 [語意化版本 2.0.0](https://semver.org/lang/zh-TW/) 中的定義，版本編號為 `主版號(major).次版號(minor).修訂號(patch)`。

以我們的這個例子來說，我的主機安裝了 2.1.0 與 2.1.1 版本，但是我們卻限定了 .NET Core SDK 使用舊版，照理說他不會去找新版的 NuGet 套件才對。但這個問題已經被確認為 `2.1.0` 的 [Bug](https://github.com/aspnet/Mvc/issues/7969)，而且也在 `2.1.1` 版正式被[修復](https://github.com/aspnet/Mvc/pull/7989)。

### 解決方案

解決方法其實很簡單，既然是 Bug，就沒甚麼好說的了，直接用新版執行 .NET Core 即可。同時，也建議將快取中的 NuGet 套件刪除，以免載入到有問題的 NuGet 套件。刪除的命令如下：

```
dotnet nuget locals all --clear
```

### 學習心得

追查這個問題，讓我了解到幾個有趣的細節。

1.  在 `.csproj` 檔案中，並沒有指定 `Microsoft.AspNetCore.App` 中繼套件的版本號！
    
    ![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/b58e5dbe20cb80c8564d-image_3138.png)
    
    意思也就是說，當專案透過 `dotnet run` 執行的時候，會以 .NET Core Runtime 預設的 `Microsoft.AspNetCore.App` 套件版本來載入。但我要怎樣知道他載入哪些套件與版本呢？
    
    我從 `dotnet build -v d` 輸出的建置紀錄中，找到許多 MSBuild 的執行細節，其中找到一個 `Sdk.DefaultItems.targets` 檔案。循著蛛絲馬跡找到 `C:\Program Files\dotnet\sdk\2.1.300\Microsoft.NETCoreSdk.BundledVersions.props` 檔案，裡面定義了 `BundledAspNetCoreAllPackageVersion` 屬性。此時真相大白，原來`Microsoft.AspNetCore.App` 中繼套件的預設版本號，是跟著 .NET Core SDK 一起走的！
    
    所以，以後我們只要安裝了新版本的 .NET Core SDK，專案什麼都不用改，直接重新建置與發行，就會是新版了！
    
2.  我要怎樣才能知道 `Microsoft.AspNetCore.App` 中繼套件到底載入了那些組件？
    
    如果你想知道 [Microsoft.AspNetCore.App](https://www.nuget.org/packages/Microsoft.AspNetCore.App/) 到底包含哪些相依的 NuGet 套件，只要到 NuGet Gallery 查詢即可。但如果想找到實際載入了哪些組件與版本，可以參考以下範例程式：
    
    ```csharp
    var referencedAssemblies = Assembly.GetExecutingAssembly().GetReferencedAssemblies();
    foreach (var assembly in referencedAssemblies)
      Console.WriteLine($"{assembly.Name} {assembly.Version}");
    ```
    

### 相關連結

-   [What's new in .NET Core 2.1 | Microsoft Docs](https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-core-2-1#roll-forward)
-   [Razor runtime compilation produces errors if running on a shared runtime that's rolled forward by pranavkm · Pull Request #7989 · aspnet/Mvc](https://github.com/aspnet/Mvc/pull/7989)
-   [Razor runtime compilation produces errors if running on a shared runtime that's rolled forward · Issue #7969 · aspnet/Mvc](https://github.com/aspnet/Mvc/issues/7969)
-   [如何在 NuGet 中管理全域套件、快取、暫存資料夾 | Microsoft Docs](https://learn.microsoft.com/zh-tw/nuget/consume-packages/managing-the-global-packages-and-cache-folders)
-   [asp.net core - How to list all installed NuGet Packages and Versions using .NET CLI - Stack Overflow](https://stackoverflow.com/questions/51698022/how-to-list-all-installed-nuget-packages-and-versions-using-net-cli/51710073)
