The Will Will Web

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

如何在專案中透過 .NET CLI 管理專案的本地工具 (.NET Local Tool)

我們在使用 .NET CLI 開發專案的時候,經常會需要安裝一些 .NET 全域工具 (.NET Global Tool),但是安裝全域工具有個小缺點,那就是這些工具會需要被註冊到 PATH 環境變數中,而且經常會有與 .NET SDK 版本不一致的問題。我在授課的時候,學員就遇過好幾次 dotnet-ef 套件跟 .NET SDK 版本不一致,導致出現靈異事件的狀況。今天這篇文章,我就來跟大家說一下如何將這些 .NET 全域工具 改裝成 .NET 本地工具

.NET Local Tool

問題描述

在使用 .NET CLI 提供的命令列工具時,若是微軟官方提供的版本,通常都會有相對應的 .NET SDK 版本。例如今天微軟剛推出 .NET Runtime 6.0.4 版本,而這個版本的 .NET Runtime 也會隨著 .NET SDK 6.0.202 一起發佈,還有 Vsual Studio 2022 也同步發行 17.1.4 版本,這個版本也同時包含了 .NET SDK 6.0.202,所以也自然附帶 .NET Runtime 6.0.4 一起發佈。有時候你不小心升級了 .NET Runtime 你都還不知道!

其實在大部分的情況下,升級 .NET Runtime 是不影響現有 .NET 程式執行的。因為 .NET 應用程式在執行的時候,會自動判斷系統中已安裝的 .NET Runtime 版本,並自動 Roll-Forward 到最新的版本執行,而且 .NET 保證在 Minor 版本更新時一定會向前相容,因此程式不會有任何狀況才對。

但是有些 .NET 重要的開發工具,其實會隨著 .NET SDK 或 .NET Runtime 一起更新,但這些工具並不會內含在 .NET SDK 之中,而是需要額外安裝 NuGet 套件才有的功能,例如以下這些:

上述三個 .NET 工具都會隨著 .NET Runtime 一起推出更新版本,但卻不會自動更新 NuGet 套件。另外還有一個 .NET 工具也會推出更新,但是永遠會少一個版本(我不知道為什麼)

上述最常用的 .NET 工具應該就是 dotnet-ef 了!

你想想看這種情境:

  1. 你在一週之前就先裝好了上課所需的 .NET 工具,假設 dotnet-ef 安裝 v6.0.3 版本
  2. 然後今天看到 Visual Studio 2022 推出新版,然後直接更新,此時 .NET SDK 就被升級到 v6.0.202,且 .NET Runtime 是 v6.0.4 版本
  3. 然後你在執行 dotnet ef 相關命令時出現錯誤訊息,你很難從訊息中得知是因為「版本」的問題導致錯誤發生!

你再想想看這種情境:

  1. 你在一週之前就先裝好了 .NET SDK v6.0.201 且 .NET Runtime 是 v6.0.3 版本

  2. 然後今天看到 Visual Studio 2022 推出新版,但是沒有套用更新!

  3. 接著你因為需要 dotnet-ef 工具,因此直接安裝這套工具(不指定版本)

    dotnet tool update --global dotnet-ef
    

    注意:使用 dotnet tool installdotnet tool update 都可以安裝 .NET 工具,若你已經安裝過此工具,透過 dotnet tool update 也可以直接更新版本。

  4. 此時你安裝的 dotnet-ef 將會是 v6.0.4 最新版本

  5. 然後你在執行 dotnet ef 相關命令時出現錯誤訊息,你很難從訊息中得知是因為「版本」的問題導致錯誤發生!

這個問題我遇過很多次了,所以會特別注意工具版本的問題!

解決方案 1:永遠使用最新版本

你只要記得更新 .NET SDK 或 Visual Studio 2022 的時候,記得也要將以下三個套件一併更新:

dotnet tool update --global dotnet-ef
dotnet tool update --global dotnet-sql-cache
dotnet tool update --global Microsoft.dotnet-openapi

是的,只有上述這三個版本可能有問題!

另外,我也會特別看一下 dotnet-aspnet-codegenerator 最新版有沒有更新,有的話就會去更新到最新版。

dotnet tool update --global dotnet-aspnet-codegenerator

其實 dotnet-aspnet-codegenerator 不是很好用,我之前有寫過兩篇文章分享過用法。(1) 如何使用 .NET CLI 快速產生 ASP․NET Core 的 Controllers 與 Views 程式碼 (2) 如何使用 VSCode 建立 ASP.NET Core 3.1 的 MVC Area 區域

解決方案 2:鎖定 .NET SDK 與 .NET 工具版本

由於少數 .NET 工具有版本的相依性,因此在開發環境鎖定 .NET SDK 與 .NET 工具版本應該是個相當合理的決定,而且若能將版本資訊也加入版控,就可以大幅減少團隊成員之間的工具版本差異,減少靈異事件的發生。

這裡我就進入本次文章的主軸,將 .NET 工具裝在本地端!

  1. 我們先建立兩個專案,一個類別庫專案、一個 Web API 專案,外加一個方案檔,最後加入版控

    # 建立方案資料夾
    mkdir DotNetLocalToolDemo
    cd DotNetLocalToolDemo
    
    # 建立 .NET 專案:一個類別庫專案、一個 Web API 專案
    dotnet new classlib -n DotNetLocalToolDemo.Lib
    dotnet new webapi -n DotNetLocalToolDemo.Api
    
    # 建立方案檔 (方便讓 Visual Studio 2022 開啟整個方案)
    dotnet new sln
    dotnet sln add ./DotNetLocalToolDemo.Lib/DotNetLocalToolDemo.Lib.csproj
    dotnet sln add ./DotNetLocalToolDemo.Api/DotNetLocalToolDemo.Api.csproj
    
    # Git 版控
    dotnet new gitignore
    git init
    git add .
    git commit -m "Initial commit"
    
  2. 我們希望在這個資料夾使用自己的 .NET SDK 與 .NET 工具版本

    # 建立 global.json 檔案
    dotnet new globaljson --sdk-version 6.0.202
    
    # 建立 .config/dotnet-tools.json 檔案
    dotnet new tool-manifest
    
    # 查詢 .NET 版本資訊 (你可以從 Host 區塊找到 Version: 6.0.4 等 .NET Runtime 版本)
    dotnet --info
    
    # Git 版控
    git add .
    git commit -m "Lock .NET SDK and create .NET Local Tool manifest file"
    
  3. 安裝 .NET 工具到 Local 並指定版本安裝

    # 注意不要加上 --global 參數就會自動安裝到本地
    dotnet tool update --version 6.0.4 dotnet-ef
    dotnet tool update --version 6.0.4 dotnet-sql-cache
    dotnet tool update --version 6.0.4 Microsoft.dotnet-openapi
    dotnet tool update --version 6.0.3 dotnet-aspnet-codegenerator
    
    # Git 版控
    git commit -am "Install .NET Tool with specific version to Local"
    

    此時工具的 NuGet 套件名稱與版本號,都會被註冊進 ./.config/dotnet-tools.json 檔案中!

    {
      "version": 1,
      "isRoot": true,
      "tools": {
        "dotnet-ef": {
          "version": "6.0.4",
          "commands": [
            "dotnet-ef"
          ]
        },
        "dotnet-sql-cache": {
          "version": "6.0.4",
          "commands": [
            "dotnet-sql-cache"
          ]
        },
        "microsoft.dotnet-openapi": {
          "version": "6.0.4",
          "commands": [
            "dotnet-openapi"
          ]
        },
        "dotnet-aspnet-codegenerator": {
          "version": "6.0.3",
          "commands": [
            "dotnet-aspnet-codegenerator"
          ]
        }
      }
    }
    

執行 .NET 工具

現在我們有了 .NET 全域工具,也有 .NET 本地工具,那工具要如何執行並選擇版本呢?我們其實有好幾種選擇!

我們以 dotnet-ef 為例,你可以有以下三種選擇:

  1. .NET 全域工具可以直接用註冊的命令來執行 (命令本身就是一個可執行檔)

    你可以先用 dotnet tool list --global 查詢 .NET 本地工具有哪些,然後用以下命令執行工具:

    dotnet-ef
    
  2. .NET 本地工具可以搭配 dotnet tool run 來執行此命令

    你可以先用 dotnet tool list 查詢 .NET 本地工具有哪些,然後用以下命令執行工具:

    dotnet tool run dotnet-ef
    

    也可以簡化成以下命令:

    dotnet dotnet-ef
    
  3. 但無論是 .NET 全域工具或 .NET 本地工具,所有命名為 dotnet-* 開頭的命令都可以簡化成以下這樣

    我們透過 dotnet 執行 dotnet-ef 命令時,可以簡化成 dotnet ef 這樣的用法:

    dotnet ef
    

    注意:這種執行方式會自動查找當前上層目錄是否有個 .config/dotnet-tools.json 檔案,如果有的話,就會優先選擇本地工具版本,否則就會選擇全域工具版本!

    你可以故意將 dotnet-ef 的 .NET 全域工具版本降低到 6.0.3 舊版,然後本地版本安裝 6.0.4 新版,使用 dotnet ef 就可以看出版本差異了!

    dotnet tool uninstall --global dotnet-ef
    dotnet tool update --global --version 6.0.3 dotnet-ef
    
    # 在 DotNetLocalToolDemo 目錄下執行會得到 6.0.4 版本
    dotnet ef --version
    
    # 在其他目錄下執行會得到 6.0.3 版本
    dotnet ef --version
    

注意事項

如果要在一台全新的電腦,剛裝好 .NET SDK 的狀態下執行 .NET 本地工具,第一次執行前要透過 dotnet tool restore 命令先還原 NuGet 套件,否則將會無法執行。

git clone https://github.com/doggy8088/DotNetLocalToolDemo.git
cd DotNetLocalToolDemo

dotnet ef

此時你應該會看到以下錯誤:

Run "dotnet tool restore" to make the "dotnet-ef" command available.

解決方案就寫在訊息中,直接執行以下命令就可以還原套件了:

dotnet tool restore

相關連結

留言評論