一文帶你快速讀懂.NET CLI!

一文帶你快速讀懂.NET CLI!

dotnet cli 是 .Net Core 功能中最有用的特性之一。在這篇文章裡,我們將介紹幾個.Net OSS 工具是如何使用 dotnet cli,並介紹如何在日常開發中使用新的 cli 工具。

正文

關鍵要點

  • dotnet cli 使得基於. Net 項目的自動化和腳本編寫變得非常簡單,尤其是與十多年前的. Net 技術相比。
  • dotnet cli 可擴展性模型創造了條件,使得通過 Nuget 將外部.NET 編寫的命令行程序集成到你的自動化構建中成為可能。
  • dotnet cli 允許在你的構建腳本中針對解決方案進行測試。
  • dotnet cli 的測試輸出有助於更好地使用持續集成 (CI)。
  • 使用 Docker 之類的容器技術比使用 dotnet cli 要容易得多。

隨著.NET Core 2.0 的發佈,微軟擁有了通用、模塊化、跨平臺和開源平臺的下一個主要版本,該版本最初於 2016 年發佈。.NET Core 已經創建了許多 API,這些 API 在.NET 框架的當前版本中是可用的。它最初是為了下一代 ASP.NET 解決方案創建的,但現在是許多其他場景的驅動和基礎,包括物聯網、雲和下一代移動解決方案。在關於.NET Core 的第二個系列的文章中,我們將進一步探討.NET Core 的優點,以及它如何不僅有益於傳統的.NET 開發人員,也有益於所有需要為市場提供強健的、高效的和經濟的解決方案的技術人員。

最近總有人問我,和那些要麼遲疑,要麼不能退出舊版本、全功能的.NET 的人相比,選擇.NET Core 的優勢是什麼?我在回答中會提到.NET Core 有更好的性能、改進的 csproj 文件格式、改進的 ASP 可測試性,並且它是跨平臺的。

作為幾個 OSS 工具 (Marten、StructureMap,以及在這個項目中作為例子被引用的Alba) 的作者,對我個人而言最大的優勢可能是dotnet cli的出現。我個人認為,結合新的.NET SDK csproj文件格式一起使用時,dotnet cli 工具使我可以更容易創建項目和維護構建腳本。我可以更容易在構建腳本中運行測試,更容易使用和分發 Nuget 包,cli 可擴展性機制非常適合將通過 Nuget 包分發的自定義可執行文件合併到自動構建中。

若要開始使用 dotnet cli,首先要在開發機器上安裝.NET SDK。安裝完成後,給你一些有用的提示:

  • 將“dotnet”工具全局安裝到你的 PATH 中,這樣在任何地方都可以通過命令行提示符使用它。
  • dotnet cli 採用 Linux 風格的命令語法,用“–word [value]”這種普通寫法表示選擇的參數,或者直接用縮寫形式“-w [value]”。如果您習慣 Git 或 Node.js 命令行工具,就不會對 dotnet cli 感到陌生。
  • “dotnet --help”將列出已安裝的命令和一些基本語法用法。
  • “dotnet --info”將告訴你使用的是哪個版本的 dotnet cli。在持續集成構建中調用此命令可能是一個好主意,以便在本地工作並在構建服務器失敗時排除故障,反之亦然。
  • 儘管我在本文中討論的是.NET Core,但是請注意,你可以在完整.NET 框架的以前版本中使用新的 SDK 項目格式和 dotnet cli。

命令行中的 Hello World

為了簡單瞭解一下 dotnet cli 的一些亮點,讓我們假設想構建一個簡單的“Hello World”ASP.NET Core 應用程序。不過,為了好玩,我們來添加一些新花樣:

1. 我們的 web 服務將在一個單獨的項目中進行自動化測試。

2. 我們將通過 Docker 容器部署我們的服務,因為這是很酷的做法 (它展示了更多的 dotnet cli)。

3. 當然,我們將盡可能多地使用 dotnet cli。

如果您想看到這段代碼的最終結果,請查看this GitHub repository。

首先,讓我們從一個名為“DotNetCliArticle”的空目錄開始,並打開您最喜歡的命令行工具到該目錄。我們將從使用“dotnet new”命令來生成解決方案文件和新項目開始。.NET SDK 附帶了幾個用於創建常見項目類型或文件的通用模板,以及其他可作為外接程序使用的模板 (稍後部分將對此進行詳細介紹)。要查看在你的機器上可用的模板,可以使用以下命令 dotnet new -help,它應該會給出如下輸出:

一文帶你快速讀懂.NET CLI!


你可能會留意到有一個 sln 模板,它針對的是空解決方案文件。我們將使用該模板,鍵入 dotnet new sln 命令,該命令將生成以下輸出:

複製代碼

The template "Solution File" was created successfully.

默認情況下,此命令將以包含的目錄命名解決方案文件。因為我將根目錄命名為為“DotNetCliArticle”,所以生成的解決方案文件是“DotNetCliArticle.sln”。

接下來,讓我們用以下命令添加“Hello,World”的實際項目:

複製代碼

dotnet new webapi --output HeyWorld

上面的命令意思是將“webapi”模板用到通過“output”參數選擇的“HeyWorld”中。這個模板將生成一個精簡的 MVC Core 項目結構,適合於無頭 API。同樣,默認的做法是根據所在的目錄命名項目文件,因此我們在目錄下得到一個名為“HeyWorld.csproj”的文件,以及所有基本文件,組成一個最小的 ASP.NET MVC Core API 項目。該模板還設置了所有必要的 Nuget 對 ASP.NET Core 的引用,我們在新項目啟動時會用到它們。

由於我剛好在一個小型 Git 存儲庫中構建了它,在使用 Git add 添加了任何新文件之後,我使用 Git status 查看新創建的文件:

複製代碼

 new file: HeyWorld/Controllers/ValuesController.cs
new file: HeyWorld/HeyWorld.csproj
new file: HeyWorld/Program.cs
new file: HeyWorld/Startup.cs
new file: HeyWorld/appsettings.Development.json
new file: HeyWorld/appsettings.json

現在,要將新項目添加到我們的空解決方案文件中,您可以像這樣使用“dotnet sln”命令:

複製代碼

dotnet sln DotNetCliArticle.sln add HeyWorld/HeyWorld.csproj

現在我們有了一個新的 ASP.NET Core API 服務作為外殼,無需打開 Visual Studio.NET(或者是 JetBrains Rider)。為了更進一步,在編寫任何實際代碼之前啟動我們的測試項目,我發出以下命令:

複製代碼

dotnet new xunit --output HeyWorld.Tests
dotnet sln DotNetCliArticle.sln add HeyWorld.Tests/HeyWorld.Tests.csproj

上面的命令使用 xUnit.NET 創建一個新項目,並將該新項目添加到我們的解決方案文件中。測試工程需要對“HeyWorld”的工程引用,幸運的是,我們可以使用很棒的“dotnet add”工具添加工程引用,如下所示:

複製代碼

dotnet add HeyWorld.Tests/HeyWorld.Tests.csproj reference HeyWorld/HeyWorld.csproj

在打開解決方案之前,我知道還有一些 Nuget 參考資料,我想在測試項目中使用它們。我選擇的斷言工具是Shoully,因此我將通過對命令行發出另一個調用來添加對最新版本的 shoully 的引用:

複製代碼

dotnet add HeyWorld.Tests/HeyWorld.Tests.csproj package Shouldly

命令行的輸出如下:

複製代碼

info : Adding PackageReference for package 'Shouldly' into project 'HeyWorld.Tests/HeyWorld.Tests.csproj'.
log : Restoring packages for /Users/jeremydmiller/code/DotNetCliArticle/HeyWorld.Tests/HeyWorld.Tests.csproj...
info : GET https://api.nuget.org/v3-flatcontainer/shouldly/index.json
info : OK https://api.nuget.org/v3-flatcontainer/shouldly/index.json 109ms
info : Package 'Shouldly' is compatible with all the specified frameworks in project 'HeyWorld.Tests/HeyWorld.Tests.csproj'.
info : PackageReference for package 'Shouldly' version '3.0.0' added to file '/Users/jeremydmiller/code/DotNetCliArticle/HeyWorld.Tests/HeyWorld.Tests.csproj'.

接下來,我想向名為 Alba 的測試項目添加至少一個 Nuget 引用。我將使用AspNetCore2來編寫針對新的 web 應用程序的 HTTP 契約測試:

複製代碼

dotnet add HeyWorld.Tests/HeyWorld.Tests.csproj package Alba.AspNetCore2

現在,在使用代碼之前先檢查一下,我將在命令行發出以下命令構建解決方案中的所有項目,確保它們都可以正常編譯:

複製代碼

dotnet build DotNetCliArticle.sln

由於 Alba.AspNetCore2 和 ASP.NET Core Nuget 在 HeyWorld 項目中的引用之間的菱形依賴版本的衝突,所以沒有編譯。不過不用擔心,因為這個問題很容易解決,只需修復 Microsoft.AspNetCore 的版本依賴關係即可。測試項目中的所有 Nuget 都是這樣的:

複製代碼

dotnet add HeyWorld.Tests/HeyWorld.Tests.csproj package Microsoft.AspNetCore.All --version 2.1.2

在上面的示例中,使用值為“2.1.2”的“–version”標誌將修復對該版本的引用,而不僅僅是使用從 Nuget 提要中找到的最新版本。

為了再次檢查我們的 Nuget 依賴問題是否已經解決,我們可以使用下面的命令進行檢查,它比重新編譯所有東西要更快:

複製代碼

dotnet clean && dotnet restore DotNetCliArticle.sln

作為一個有經驗的.NET 開發人員,我非常擔心臨時 /obj 和 /bin 文件夾中殘留的文件。因此,我在 Visual Studio 中使用“Clean Solution”命令,以防我試圖改變引用的時候落下些什麼。從命令行執行“dotnet clean”命令是完全相同的操作。

同樣,針對眾所周知的 Nuget 依賴問題,“dotnet restore”命令在解決方案中都試著去解決了。在這種情況下,使用“dotnet restore”可以讓我們快速發現任何潛在的衝突或丟失的 Nuget 引用,而無需進行完整的編譯,我在自己的工作中主要就採用該命令。在最新版本的 dotnet cli 中,在調用“dotnet build/test/pack/etc”時,會自動為您完成 Nuget 解析 (該行為可以用標記覆蓋),這將首先需要 Nuget。

我們調用的“dotnet restore DotNetCliArticle.sln”乾淨利落地運行完畢,沒有錯誤,所以我們終於可以準備編寫一些代碼了。讓我們打開您選擇的 C# 編輯器,向 HeyWorld 添加一個代碼文件。測試項目包含一個非常簡單的 HTTP 協議測試,它將指定我們希望從新的 HeyWorld 應用程序中的“GET: /”路由獲得的行為:

複製代碼

using System.Threading.Tasks;
using Alba;
using Xunit;
namespace HeyWorld.Tests
{
public class verify_the_endpoint
{
[Fact]
public async Task check_it_out()
{
using (var system = SystemUnderTest.ForStartup<startup>())
{
await system.Scenario(s =>
{
s.Get.Url("/");
s.ContentShouldBe("Hey, world.");
s.ContentTypeShouldBe("text/plain; charset=utf-8");
});
}
}

}
}
/<startup>

結果文件應該保存在具有適當名稱 (如 verify_the_endpoints.cs) 的 HeyWorld.Tests 目錄。

在沒有深入瞭解 Alba 機制前,我們的新 HeyWorld 應用的首頁路由應該寫出“Hey, world”。雖然我們還沒有在 HeyWorld 應用中編寫任何實際的代碼,但是我們仍然可以運行這個測試,看看它能夠連接正確,還是因為某些“理所當然的理由”而失敗。

回到命令行,我可以使用以下命令運行測試項目中的所有測試:

複製代碼

dotnet test HeyWorld.Tests/HeyWorld.Tests.csproj

我們的一個測試會失敗,因為還沒有實現任何東西,它給了我們這樣的輸出:

複製代碼

Build started, please wait...
Build completed.
Test run for /Users/jeremydmiller/code/DotNetCliArticle/HeyWorld.Tests/bin/Debug/netcoreapp2.1/HeyWorld.Tests.dll(.NETCoreApp,Version=v2.1)
Microsoft (R) Test Execution Command Line Tool Version 15.7.0
Copyright (c) Microsoft Corporation. All rights reserved.
Starting test execution, please wait...
Total tests: 1. Passed: 1. Failed: 0. Skipped: 0.
Test Run Successful.
Test execution time: 2.4565 Seconds

為了把輸出求和,執行了一個測試,但是失敗了。我們還可以看到標準的 xUnit 輸出,它提供了一些關於測試失敗原因的信息。這裡需要注意的是,“dotnet test”命令將返回一個退出代碼,如果所有測試都通過,則返回 0,表示成功;如果任何測試失敗,則返回一個非零退出代碼,表示失敗。這對於持續集成 (CI) 腳本非常重要,大多數 CI 工具使用任何命令的退出代碼來確定構建何時失敗。

我認為上面的測試之所以失敗是因為“理所當然的原因”,這意味著測試工具似乎能夠引導真正的應用程序,我希望得到 404 響應,因為還沒有編寫任何代碼。接下來,讓我們為預期的行為實現一個 MVC Core 端點:

複製代碼

public class HomeController : Controller
{
[HttpGet("/")]
public string SayHey()
{
return "Hey, world!";
}
}

(注意,前面的代碼應該作為 HeyWorld\startup.cs 文件中的附加類添加)

再次回到命令行,讓我們運行前面的“dotnet test HeyWorld.Tests/HeyWorld.Tests.csproj”命令,希望看到這樣的結果:

複製代碼

Build started, please wait...
Build completed.
Test run for /Users/jeremydmiller/code/DotNetCliArticle/HeyWorld.Tests/bin/Debug/netcoreapp2.1/HeyWorld.Tests.dll(.NETCoreApp,Version=v2.1)
Microsoft (R) Test Execution Command Line Tool Version 15.7.0
Copyright (c) Microsoft Corporation. All rights reserved.
Starting test execution, please wait...
Total tests: 1. Passed: 1. Failed: 0. Skipped: 0.
Test Run Successful.
Test execution time: 2.4565 Seconds

好了,現在測試通過了,讓我們運行實際的應用程序。由於“dotnet new webapi”模板使用進程內的 in-process Kestrel web server 來處理 HTTP 請求,所以要運行新的 HeyWorld 應用程序,我們唯一需要做的一件事就是從命令行使用以下命令啟動它:

複製代碼

 dotnet run --project HeyWorld/HeyWorld.csproj

運行上面的命令應該會得到如下輸出:

複製代碼

Using launch settings from HeyWorld/Properties/launchSettings.json...
: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0]
User profile is available. Using '/Users/jeremydmiller/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest.
Hosting environment: Development
Content root path: /Users/jeremydmiller/code/DotNetCliArticle/HeyWorld
Now listening on: https://localhost:5001
Now listening on: http://localhost:5000
Application started. Press Ctrl+C to shut down.

要測試我們現在正在運行的新應用程序,只需在瀏覽器中導航如下:

一文帶你快速讀懂.NET CLI!


處理 HTTPS 設置超出了本文的範圍。

請再次注意,我假設所有命令都是在將當前目錄設置為解決方案根文件夾的情況下執行的。如果當前目錄是一個項目目錄,並且只有一個 *.csproj。那麼,您只需在該目錄下鍵入“dotnet run”即可。現在我們已經有了一個經過測試的 web api 應用程序,接下來讓我們將 HeyWorld 放到 Docker 鏡像中。使用 the standard template for dockerizing a .NET Core application,我們將向 HeyWorld 項目添加一個 Dockerfile,內容如下:

複製代碼

FROM microsoft/dotnet:sdk AS build-env
WORKDIR /app
Copy csproj and restore as distinct layers
COPY *.csproj ./
RUN dotnet restore
Copy everything else and build
COPY . ./
RUN dotnet publish -c Release -o out
Build runtime image
FROM microsoft/dotnet:aspnetcore-runtime
WORKDIR /app
COPY --from=build-env /app/out .
ENTRYPOINT ["dotnet", "HeyWorld.dll"]

(注意,前面的文本應該保存到項目目錄中名為 Dockerfile 的文本文件中——在本例中是 HeyWorld\Dockerfile)。

因為這篇文章僅僅是關於 dotnet cli 的,我只想關注 Dockerfile 中它的兩種用法:

1.“dotnet restore”–正如我們在上面學到的,這個命令將解決應用程序的任何 Nuget 依賴關係。

2.“dotnet publish -c Release -o out”–“dotnet publish”命令將構建指定的項目,並將組成應用程序的所有文件複製到給定位置。在我們的例子中,“dotnet publish”將為 HeyWorld 本身複製已編譯的程序集、從 Nuget 依賴項引用的所有程序集、配置文件以及 csproj 文件中引用的任何文件。

請注意,在上面的用法中,我們必須通過使用“-c Release”標誌明確地告知“dotnet publish”用“Release”配置編譯。那些用於編碼的 dotnet cli 命令 (例如“build”、“publish”、“pack”)如果沒有指定,將以 “Debug”為默認值。注意這種行為,如果要發佈用於生產的 Nuget 或應用程序,請記住指定“-c Release”或“-configuration Release”。別怪我沒提醒你。

為了完成整個週期,我們現在可以使用以下命令通過 Docker 構建和部署我們的小 HeyWorld 應用程序:

複製代碼

docker build -t heyworld .
docker run -d -p 8080:80 --name myapp heyworld

第一個命令為我們的應用程序“heyworld”構建並本地發佈 Docker 鏡像。第二個命令實際上作為一個名為“myapp”的 Docker 容器運行我們的應用程序。您可以打開瀏覽器訪問“http://localhost:8080”予以驗證。

總結

dotnet cli 使得基於. NET 項目的自動化和腳本編寫變得非常簡單,尤其是與十多年前的.NET 技術相比。在許多情況下,您甚至可能會避開任何基於任務的構建腳本工具 (Cake、Fake、Rake、Psake 等),而選擇只委託給 dotnet cli 的簡單 shell 腳本。此外,dotnet cli 可擴展性模型可以很容易地將外部.NET 授權的命令行應用程序通過 Nuget 分佈到自動構建程序中。

關於作者傑里米·米勒 (Jeremy Miller)在密蘇里州一個農場社區長大,那裡有一群“特別”的人,名叫“樹蔭技工”。通常,他們不是世界上最有名望的人,但他們有解決機械問題的訣竅,而且做事魯莽無畏。如果你發現從一輛停在街區的通勤車下伸出來兩條腿,那他想必就是名修理工了,他的周圍是一些骨架車,堆擠在他那長滿灌木、堆滿垃圾的院子裡。你看到的被遺棄在他周圍的打漿機並不是沒用的,他們是素材。他會零零碎碎地進行些小調整,然後根據你的需要想出一個創造性的解決方案。儘管名聲一般,但一個樹蔭技工知道如何讓東西運行。雖然米勒沒有任何特殊的機械能力 (儘管他擁有機械工程學位),但他喜歡把自己當成一個像樹蔭技工似的開發人員。他的硬盤上肯定到處都是廢棄的開源項目碎片。隨著.NET Core 2.0 的發佈,微軟擁有了通用、模塊化、跨平臺和開源平臺的下一個主要版本,該版本最初於 2016 年發佈。.NET Core 已經創建了許多 API,這些 API 在.NET 框架的當前版本中是可用的。它最初是為了下一代 ASP.NET 解決方案創建的,但現在是許多其他場景的驅動和基礎,包括物聯網、雲和下一代移動解決方案。在關於.NET Core 的第二個系列的文章中,我們將進一步探討.NET Core 的優點,以及它如何不僅有益於傳統的.NET 開發人員,也有益於所有需要為市場提供強健的、高效的和經濟的解決方案的技術人員。

查看英文原文:A Quick Tour of the .NET CLI


分享到:


相關文章: