Netcore【】

正文
關(guān)鍵要點dotnet cli 使得基于. Net 項目的自動化和腳本編寫變得非常簡單,尤其是與十多年前的. Net 技術(shù)相比。dotnet cli 可擴展性模型創(chuàng)造了條件,使得通過 Nuget 將外部.NET 編寫的命令行程序集成到你的自動化構(gòu)建中成為可能。dotnet cli 允許在你的構(gòu)建腳本中針對解決方案進行測試。dotnet cli 的測試輸出有助于更好地使用持續(xù)集成 (CI)。使用 Docker 之類的容器技術(shù)比使用 dotnet cli 要容易得多。
隨著.NET Core 2.0 的發(fā)布,微軟擁有了通用、模塊化、跨平臺和開源平臺的下一個主要版本,該版本最初于 2016 年發(fā)布。.NET Core 已經(jīng)創(chuàng)建了許多 API,這些 API 在.NET 框架的當前版本中是可用的。它最初是為了下一代 ASP.NET 解決方案創(chuàng)建的,但現(xiàn)在是許多其他場景的驅(qū)動和基礎,包括物聯(lián)網(wǎng)、云和下一代移動解決方案。在關(guān)于.NET Core 的第二個系列的文章中,我們將進一步探討.NET Core 的優(yōu)點,以及它如何不僅有益于傳統(tǒng)的.NET 開發(fā)人員,也有益于所有需要為市場提供強健的、高效的和經(jīng)濟的解決方案的技術(shù)人員。
最近總有人問我,和那些要么遲疑,要么不能退出舊版本、全功能的.NET 的人相比,選擇.NET Core 的優(yōu)勢是什么?我在回答中會提到.NET Core 有更好的性能、改進的 csproj 文件格式、改進的 ASP 可測試性,并且它是跨平臺的。
作為幾個 OSS 工具 (Marten、StructureMap,以及在這個項目中作為例子被引用的Alba) 的作者,對我個人而言最大的優(yōu)勢可能是dotnet cli的出現(xiàn)。我個人認為,結(jié)合新的.NET SDK csproj文件格式一起使用時,dotnet cli 工具使我可以更容易創(chuàng)建項目和維護構(gòu)建腳本。我可以更容易在構(gòu)建腳本中運行測試,更容易使用和分發(fā) Nuget 包,cli 可擴展性機制非常適合將通過 Nuget 包分發(fā)的自定義可執(zhí)行文件合并到自動構(gòu)建中。
若要開始使用 dotnet cli,首先要在開發(fā)機器上安裝.NET SDK。安裝完成后,給你一些有用的提示:將“dotnet”工具全局安裝到你的 PATH 中,這樣在任何地方都可以通過命令行提示符使用它。dotnet cli 采用 Linux 風格的命令語法,用“–word [value]”這種普通寫法表示選擇的參數(shù),或者直接用縮寫形式“-w [value]”。如果您習慣 Git 或 Node.js 命令行工具,就不會對 dotnet cli 感到陌生。“dotnet --help”將列出已安裝的命令和一些基本語法用法。“dotnet --info”將告訴你使用的是哪個版本的 dotnet cli。在持續(xù)集成構(gòu)建中調(diào)用此命令可能是一個好主意,以便在本地工作并在構(gòu)建服務器失敗時排除故障,反之亦然。盡管我在本文中討論的是.NET Core,但是請注意,你可以在完整.NET 框架的以前版本中使用新的 SDK 項目格式和 dotnet cli。
命令行中的 Hello World
為了簡單了解一下 dotnet cli 的一些亮點,讓我們假設想構(gòu)建一個簡單的“Hello World”ASP.NET Core 應用程序。不過,為了好玩,我們來添加一些新花樣:
1. 我們的 web 服務將在一個單獨的項目中進行自動化測試。
2. 我們將通過 Docker 容器部署我們的服務,因為這是很酷的做法 (它展示了更多的 dotnet cli)。
3. 當然,我們將盡可能多地使用 dotnet cli。
如果您想看到這段代碼的最終結(jié)果,請查看this GitHub repository。
首先,讓我們從一個名為“DotNetCliArticle”的空目錄開始,并打開您最喜歡的命令行工具到該目錄。我們將從使用“dotnet new”命令來生成解決方案文件和新項目開始。.NET SDK 附帶了幾個用于創(chuàng)建常見項目類型或文件的通用模板,以及其他可作為外接程序使用的模板 (稍后部分將對此進行詳細介紹)。要查看在你的機器上可用的模板,可以使用以下命令 dotnet new -help,它應該會給出如下輸出:
你可能會留意到有一個 sln 模板,它針對的是空解決方案文件。我們將使用該模板,鍵入 dotnet new sln 命令,該命令將生成以下輸出:
復制代碼
The template "Solution File" was created successfully.
默認情況下,此命令將以包含的目錄命名解決方案文件。因為我將根目錄命名為為“DotNetCliArticle”,所以生成的解決方案文件是“DotNetCliArticle.sln”。
接下來,讓我們用以下命令添加“Hello,World”的實際項目:
復制代碼
dotnet new webapi --output HeyWorld
上面的命令意思是將“webapi”模板用到通過“output”參數(shù)選擇的“HeyWorld”中。這個模板將生成一個精簡的 MVC Core 項目結(jié)構(gòu),適合于無頭 API。同樣,默認的做法是根據(jù)所在的目錄命名項目文件,因此我們在目錄下得到一個名為“HeyWorld.csproj”的文件,以及所有基本文件,組成一個最小的 ASP.NET MVC Core API 項目。該模板還設置了所有必要的 Nuget 對 ASP.NET Core 的引用,我們在新項目啟動時會用到它們。
由于我剛好在一個小型 Git 存儲庫中構(gòu)建了它,在使用 Git add 添加了任何新文件之后,我使用 Git status 查看新創(chuàng)建的文件:
復制代碼
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
現(xiàn)在,要將新項目添加到我們的空解決方案文件中,您可以像這樣使用“dotnet sln”命令:
復制代碼
dotnet sln DotNetCliArticle.sln add HeyWorld/HeyWorld.csproj
現(xiàn)在我們有了一個新的 ASP.NET Core API 服務作為外殼,無需打開 Visual Studio.NET(或者是 JetBrains Rider)。為了更進一步,在編寫任何實際代碼之前啟動我們的測試項目,我發(fā)出以下命令:
復制代碼
dotnet new xunit --output HeyWorld.Testsdotnet sln DotNetCliArticle.sln add HeyWorld.Tests/HeyWorld.Tests.csproj
上面的命令使用 xUnit.NET 創(chuàng)建一個新項目,并將該新項目添加到我們的解決方案文件中。測試工程需要對“HeyWorld”的工程引用,幸運的是,我們可以使用很棒的“dotnet add”工具添加工程引用,如下所示:
復制代碼
dotnet add HeyWorld.Tests/HeyWorld.Tests.csproj reference HeyWorld/HeyWorld.csproj
在打開解決方案之前,我知道還有一些 Nuget 參考資料,我想在測試項目中使用它們。我選擇的斷言工具是Shoully,因此我將通過對命令行發(fā)出另一個調(diào)用來添加對最新版本的 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.jsoninfo : OK https://api.nuget.org/v3-flatcontainer/shouldly/index.json 109msinfo : 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
現(xiàn)在,在使用代碼之前先檢查一下,我將在命令行發(fā)出以下命令構(gòu)建解決方案中的所有項目,確保它們都可以正常編譯:
復制代碼
dotnet build DotNetCliArticle.sln
由于 Alba.AspNetCore2 和 ASP.NET Core Nuget 在 HeyWorld 項目中的引用之間的菱形依賴版本的沖突,所以沒有編譯。不過不用擔心,因為這個問題很容易解決,只需修復 Microsoft.AspNetCore 的版本依賴關(guān)系即可。測試項目中的所有 Nuget 都是這樣的:
復制代碼
dotnet add HeyWorld.Tests/HeyWorld.Tests.csproj package Microsoft.AspNetCore.All --version 2.1.2
在上面的示例中,使用值為“2.1.2”的“–version”標志將修復對該版本的引用,而不僅僅是使用從 Nuget 提要中找到的最新版本。
為了再次檢查我們的 Nuget 依賴問題是否已經(jīng)解決,我們可以使用下面的命令進行檢查,它比重新編譯所有東西要更快:
復制代碼
dotnet clean && dotnet restore DotNetCliArticle.sln
作為一個有經(jīng)驗的.NET 開發(fā)人員,我非常擔心臨時 /obj 和 /bin 文件夾中殘留的文件。因此,我在 Visual Studio 中使用“Clean Solution”命令,以防我試圖改變引用的時候落下些什么。從命令行執(zhí)行“dotnet clean”命令是完全相同的操作。
同樣,針對眾所周知的 Nuget 依賴問題,“dotnet restore”命令在解決方案中都試著去解決了。在這種情況下,使用“dotnet restore”可以讓我們快速發(fā)現(xiàn)任何潛在的沖突或丟失的 Nuget 引用,而無需進行完整的編譯,我在自己的工作中主要就采用該命令。在最新版本的 dotnet cli 中,在調(diào)用“dotnet build/test/pack/etc”時,會自動為您完成 Nuget 解析 (該行為可以用標記覆蓋),這將首先需要 Nuget。
我們調(diào)用的“dotnet restore DotNetCliArticle.sln”干凈利落地運行完畢,沒有錯誤,所以我們終于可以準備編寫一些代碼了。讓我們打開您選擇的 C# 編輯器,向 HeyWorld 添加一個代碼文件。測試項目包含一個非常簡單的 HTTP 協(xié)議測試,它將指定我們希望從新的 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"); }); } } }}
結(jié)果文件應該保存在具有適當名稱 (如 verify_the_endpoints.cs) 的 HeyWorld.Tests 目錄。
在沒有深入了解 Alba 機制前,我們的新 HeyWorld 應用的首頁路由應該寫出“Hey, world”。雖然我們還沒有在 HeyWorld 應用中編寫任何實際的代碼,但是我們?nèi)匀豢梢赃\行這個測試,看看它能夠連接正確,還是因為某些“理所當然的理由”而失敗。
回到命令行,我可以使用以下命令運行測試項目中的所有測試:
復制代碼
dotnet test HeyWorld.Tests/HeyWorld.Tests.csproj
我們的一個測試會失敗,因為還沒有實現(xiàn)任何東西,它給了我們這樣的輸出:
復制代碼
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.0Copyright (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
為了把輸出求和,執(zhí)行了一個測試,但是失敗了。我們還可以看到標準的 xUnit 輸出,它提供了一些關(guān)于測試失敗原因的信息。這里需要注意的是,“dotnet test”命令將返回一個退出代碼,如果所有測試都通過,則返回 0,表示成功;如果任何測試失敗,則返回一個非零退出代碼,表示失敗。這對于持續(xù)集成 (CI) 腳本非常重要,大多數(shù) CI 工具使用任何命令的退出代碼來確定構(gòu)建何時失敗。
我認為上面的測試之所以失敗是因為“理所當然的原因”,這意味著測試工具似乎能夠引導真正的應用程序,我希望得到 404 響應,因為還沒有編寫任何代碼。接下來,讓我們?yōu)轭A期的行為實現(xiàn)一個 MVC Core 端點:
復制代碼
public class HomeController : Controller{ [HttpGet("/")] public string SayHey() { return "Hey, world!"; }}
(注意,前面的代碼應該作為 HeyWorldstartup.cs 文件中的附加類添加)
再次回到命令行,讓我們運行前面的“dotnet test HeyWorld.Tests/HeyWorld.Tests.csproj”命令,希望看到這樣的結(jié)果:
復制代碼
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.0Copyright (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
好了,現(xiàn)在測試通過了,讓我們運行實際的應用程序。由于“dotnet new webapi”模板使用進程內(nèi)的 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: DevelopmentContent root path: /Users/jeremydmiller/code/DotNetCliArticle/HeyWorldNow listening on: https://localhost:5001Now listening on: http://localhost:5000Application started. Press Ctrl C to shut down.
要測試我們現(xiàn)在正在運行的新應用程序,只需在瀏覽器中導航如下:
處理 HTTPS 設置超出了本文的范圍。
請再次注意,我假設所有命令都是在將當前目錄設置為解決方案根文件夾的情況下執(zhí)行的。如果當前目錄是一個項目目錄,并且只有一個 *.csproj。那么,您只需在該目錄下鍵入“dotnet run”即可。現(xiàn)在我們已經(jīng)有了一個經(jīng)過測試的 web api 應用程序,接下來讓我們將 HeyWorld 放到 Docker 鏡像中。使用 the standard template for dockerizing a .NET Core application,我們將向 HeyWorld 項目添加一個 Dockerfile,內(nèi)容如下:
復制代碼
FROM microsoft/dotnet:sdk AS build-envWORKDIR /appCopy csproj and restore as distinct layersCOPY *.csproj ./RUN dotnet restoreCopy everything else and buildCOPY . ./RUN dotnet publish -c Release -o outBuild runtime imageFROM microsoft/dotnet:aspnetcore-runtimeWORKDIR /appCOPY --from=build-env /app/out .ENTRYPOINT ["dotnet", "HeyWorld.dll"]
(注意,前面的文本應該保存到項目目錄中名為 Dockerfile 的文本文件中——在本例中是 HeyWorldDockerfile)。
因為這篇文章僅僅是關(guān)于 dotnet cli 的,我只想關(guān)注 Dockerfile 中它的兩種用法:
1.“dotnet restore”–正如我們在上面學到的,這個命令將解決應用程序的任何 Nuget 依賴關(guān)系。
2.“dotnet publish -c Release -o out”–“dotnet publish”命令將構(gòu)建指定的項目,并將組成應用程序的所有文件復制到給定位置。在我們的例子中,“dotnet publish”將為 HeyWorld 本身復制已編譯的程序集、從 Nuget 依賴項引用的所有程序集、配置文件以及 csproj 文件中引用的任何文件。
請注意,在上面的用法中,我們必須通過使用“-c Release”標志明確地告知“dotnet publish”用“Release”配置編譯。那些用于編碼的 dotnet cli 命令 (例如“build”、“publish”、“pack”)如果沒有指定,將以 “Debug”為默認值。注意這種行為,如果要發(fā)布用于生產(chǎn)的 Nuget 或應用程序,請記住指定“-c Release”或“-configuration Release”。別怪我沒提醒你。
為了完成整個周期,我們現(xiàn)在可以使用以下命令通過 Docker 構(gòu)建和部署我們的小 HeyWorld 應用程序:
復制代碼
docker build -t heyworld .docker run -d -p 8080:80 --name myapp heyworld
第一個命令為我們的應用程序“heyworld”構(gòu)建并本地發(fā)布 Docker 鏡像。第二個命令實際上作為一個名為“myapp”的 Docker 容器運行我們的應用程序。您可以打開瀏覽器訪問“http://localhost:8080”予以驗證。
總結(jié)
dotnet cli 使得基于. NET 項目的自動化和腳本編寫變得非常簡單,尤其是與十多年前的.NET 技術(shù)相比。在許多情況下,您甚至可能會避開任何基于任務的構(gòu)建腳本工具 (Cake、Fake、Rake、Psake 等),而選擇只委托給 dotnet cli 的簡單 shell 腳本。此外,dotnet cli 可擴展性模型可以很容易地將外部.NET 授權(quán)的命令行應用程序通過 Nuget 分布到自動構(gòu)建程序中。
關(guān)于作者杰里米·米勒 (Jeremy Miller)在密蘇里州一個農(nóng)場社區(qū)長大,那里有一群“特別”的人,名叫“樹蔭技工”。通常,他們不是世界上最有名望的人,但他們有解決機械問題的訣竅,而且做事魯莽無畏。如果你發(fā)現(xiàn)從一輛停在街區(qū)的通勤車下伸出來兩條腿,那他想必就是名修理工了,他的周圍是一些骨架車,堆擠在他那長滿灌木、堆滿垃圾的院子里。你看到的被遺棄在他周圍的打漿機并不是沒用的,他們是素材。他會零零碎碎地進行些小調(diào)整,然后根據(jù)你的需要想出一個創(chuàng)造性的解決方案。盡管名聲一般,但一個樹蔭技工知道如何讓東西運行。雖然米勒沒有任何特殊的機械能力 (盡管他擁有機械工程學位),但他喜歡把自己當成一個像樹蔭技工似的開發(fā)人員。他的硬盤上肯定到處都是廢棄的開源項目碎片。隨著.NET Core 2.0 的發(fā)布,微軟擁有了通用、模塊化、跨平臺和開源平臺的下一個主要版本,該版本最初于 2016 年發(fā)布。.NET Core 已經(jīng)創(chuàng)建了許多 API,這些 API 在.NET 框架的當前版本中是可用的。它最初是為了下一代 ASP.NET 解決方案創(chuàng)建的,但現(xiàn)在是許多其他場景的驅(qū)動和基礎,包括物聯(lián)網(wǎng)、云和下一代移動解決方案。在關(guān)于.NET Core 的第二個系列的文章中,我們將進一步探討.NET Core 的優(yōu)點,以及它如何不僅有益于傳統(tǒng)的.NET 開發(fā)人員,也有益于所有需要為市場提供強健的、高效的和經(jīng)濟的解決方案的技術(shù)人員。