.NET CLI簡單教程和專案結構

sogeisetsu發表於2021-10-26

WHAT IS .NET CLI ?

.NET 命令列介面 (CLI) 工具是用於開發、生成、執行和釋出 .NET 應用程式的跨平臺工具鏈。


來源:.NET CLI | Microsoft Docs

.NET CLI是.NET官方的一個命令列工具。本文將介紹.NET CLI的幾個主要的命令。並通過這幾個命令來了解.NET控制檯程式的專案結構。

不建議在學習階段使用IDE

IDE是一種非常好的程式設計工具,但是,在初學階段,並不建議使用IDE,因為IDE會讓學習的人喪失對程式第一手的感知,有相當多的人在使用高階的框架開發的時候會出現沒有IDE就不會開發的現象,我對IDE的看法是:“開發需要依賴IDE,但是不能依靠IDE”,也就是說,在沒有IDE的情況下要依然能夠會編寫程式,會執行和釋出程式。

沒有IDE,那麼就需要用.NET CLI建立、執行、構建和釋出程式,用文字編寫軟體編寫程式。文字編寫軟體可以用系統自帶的記事本,但是更建議使用帶“程式碼高亮和方法跳轉”的文字編寫軟體,比如github開發的Atom或者微軟的vscode

微軟的Visual Studio 2019是一個比較好的.NET開發IDE。它的用法將在後面講到。

事實上,強大的IDE(Visual Studio)在進行構建、編譯和釋出程式等操作的時候,依靠的也是.NET CLI

下載並安裝.NET CLI

.NET CLI.NET SDK的一部分,下載.NET CLI的前提是去下載.NET SDK.NET SDK 是一組用於開發和執行 .NET 應用程式的庫和工具。我們一般意義上講的.NET就是指的.NET SDK

.NET 命令列介面 (CLI) 工具是用於開發、生成、執行和釋出 .NET 應用程式的跨平臺工具鏈。

.NET CLI 附帶了 .NET SDK。 若要了解如何安裝 .NET SDK,請參閱安裝 .NET Core


來源:.NET CLI | Microsoft Docs

如果在CMD輸入dotnet --version之後,有正確的關於dotnet版本的回應的話,那麼就證明.NET SDK已經安裝成功,作為.NET SDK一部分的.NET CLI也已經安裝完畢。

(base) PS C:\Users\蘇月晟\Desktop> dotnet --version
5.0.401

安裝文字編輯器

前面已經講了不建議在學習階段使用高階的IDE,所以推薦一個好用的文字編輯器——Visual Studio Code。簡稱“vscode”。

vscode是微軟的一款開源產品,它帶有程式碼高亮功能,支援豐富的外掛,可以直接呼叫終端,也可以直接在終端裡面通過code命令直接呼叫vscode。更重要的是微軟官方文件裡面將.NET CLI的例項講解和vscode的使用放在了一起。

下載安裝完成之後,安裝三個外掛,安裝方式是點選左側的外掛按鈕,進行搜尋安裝。

三個外掛如下:

  • Chinese (Simplified) (簡體中文) Language Pack for Visual Studio Code
  • C#
  • C# XML Documentation Comments

這三個外掛,一個是將vscode介面翻譯成中文,一個是為編寫和除錯C#程式碼提供支援,最後一個外掛是可以自動生成類註釋和方法註釋的模板。

.NET CLI 初級命令

初級命令就是newrunbuildpublish

dotnet new

根據指定的模板,建立新的專案、配置檔案或解決方案。

dotnet new <TEMPLATE> [--dry-run] [--force] [-lang|--language {"C#"|"F#"|VB}]
    [-n|--name <OUTPUT_NAME>] [-o|--output <OUTPUT_DIRECTORY>] [Template options]

dotnet new -h|--help

常見的命令是dotnet new console -o <appname>

console是模板的名稱,代表的是控制檯程式。可以執行 dotnet new --list 以檢視所有已安裝模板的列表。

常用命令選項

  • -o|--output <OUTPUT_DIRECTORY>

    指定的是專案的輸出目錄,同時也代表了專案的名稱。

  • -n|--name <OUTPUT_NAME>

    指定所建立的輸出的名稱。 如果未指定名稱,使用的是當前目錄的名稱。

  • -lang|--language {C#|F#|VB}

    一般情況下預設的語言就是C#

專案結構

假設使用dotnet new console -o MyAppOne建立了一個名為MyAppOne的控制檯應用,那麼在沒有經過編譯執行的情況下MyAppOne資料夾下面的結構應該是這樣的:

.
├── MyAppOne.csproj
├── Program.cs
└── obj

每個檔案的作用

  • MyAppOne.csproj

    這個檔案是專案組織和配置的核心,會在生成時被MSBuild用來作為輸入

  • **obj **

    資料夾包含了編譯與生成時需要的和產生的中間檔案,一般不需要關注。

  • Progam.cs

    就是程式的程式碼檔案,裡面已經寫了一段最基本的HelloWord程式碼。

csproj 檔案的本質是一個儲存專案資訊的xml檔案。預設配置如下:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>

</Project>

專案系統和 MSBuild

.NET 應用是使用 MSBuild 從原始碼中生成的。 專案檔案(.csproj、.fsproj 或 .vbproj)指定目標和負責編譯、打包和釋出程式碼的關聯任務 。 有引用目標和任務的標準集合的 SDK 識別符號。 使用這些識別符號有助於使專案檔案較小且易於使用。

關於MSBuild的常用屬性和命令列將在後面講到。

dotnet run

無需任何顯式編譯或啟動命令即可執行原始碼。

dotnet run [-a|--arch <ARCHITECTURE>] [-c|--configuration <CONFIGURATION>]
    [-f|--framework <FRAMEWORK>] [--force] [--interactive]
    [--launch-profile <NAME>] [--no-build]
    [--no-dependencies] [--no-launch-profile] [--no-restore]
    [--os <OS>] [--project <PATH>] [-r|--runtime <RUNTIME_IDENTIFIER>]
    [-v|--verbosity <LEVEL>] [[--] [application arguments]]

dotnet run -h|--help

dotnet run的本質其實上是編譯並執行。編譯這一步它其實是相當於使用dotnet build,執行這一步相當於執行依賴於框架的應用程式 DLL,比如dotnet MyAppOne.dll

命令取決於生成程式碼的 dotnet build 命令。 對於此生成的任何要求,例如專案必須首先還原,同樣適用於 dotnet run


來源:dotnet run 命令 - .NET CLI | Microsoft Docs

輸出檔案會寫入到預設位置,即 bin/<configuration>/<target>。 例如,如果具有 netcoreapp2.1 應用程式並且執行 dotnet run,則輸出置於 bin/Debug/netcoreapp2.1。 將根據需要覆蓋檔案。 臨時檔案將置於 obj 目錄。

常用命令選項

  • -c|--configuration <CONFIGURATION>

    定義生成配置。 大多數專案的預設配置為 Debug,但你可以覆蓋專案中的生成配置設定。也就是說configuration預設是Debug,執行dotnet run之後的輸出檔案會在bin/Debug之下。

  • -f|--framework <FRAMEWORK>

    使用指定框架生成並執行應用。 框架必須在專案檔案中進行指定。一般情況下不需要在命令列中指定框架,只要在MSBuild檔案(.csproj)檔案裡面指定一個<TargetFramework>就可以了,如果該專案指定多個框架,在不使用 -f|--framework <FRAMEWORK> 選項指定框架時,執行 dotnet run 將導致錯誤。也就是說,一般情況下,預設的框架是生成專案的.NET SDK的版本,我安裝的是,NET 5.0,那麼我又沒有更改<TargetFramework>,那麼執行dotnet run之後的輸出檔案會在bin/Debug/net5.0之下。

一般情況下,是在專案根目錄執行dotnet run

專案結構

嘗試對在dotnet new一節中生成的控制檯專案MyAppOne的根目錄執行dotnet run。會執行預設的Program.cs,輸出Hello World!

專案結構會增加一個bin目錄:

.
├── MyAppOne.csproj
├── Program.cs
├── bin
└── obj

bin目錄的結構如下:

.
└── Debug
    └── net5.0
        ├── MyAppOne.deps.json
        ├── MyAppOne.dll
        ├── MyAppOne.exe
        ├── MyAppOne.pdb
        ├── MyAppOne.runtimeconfig.dev.json
        ├── MyAppOne.runtimeconfig.json
        └── ref
            └── MyAppOne.dll

為什麼執行dotnet run之後的輸出檔案會在bin/Debug/net 5.0之下,已經在前面dotnet run的選項裡講的很清楚了,就不再贅述。下面講一下輸出檔案的每一個檔案的作用。

每個檔案的作用

  • MyAppOne.deps.json

    儲存一個依賴的列表,也就是編譯上下文資料和編譯依賴,不是技術上需要的,但是在使用服務,包快取或共享這些安裝功能的時候是需要的。這個檔案由程式進行處理和使用,使用者不應編輯。

  • MyAppOne.dll

    託管程式集,包括符合 ECMA 的入口點令牌,通俗說就是程式本體。執行它需要dotnet MyAppOne.dll

  • MyAppOne.exe

    可執行檔案,雙擊就能執行。這個東西應該不用多解釋了,使用過windows的人應該對exe不陌生。

  • MyAppOne.runtimeconfig.json

    包含執行時配置設定的可選配置檔案。這個可選可不是說這個檔案可有可無,它很重要,甚至缺少他之後甚至無法執行DLL程式,這個可選指的是再這個檔案裡面可以對相關配置進行選擇。

  • MyAppOne.pdb

    該檔案記錄了程式碼中斷點等除錯資訊,沒有它,打的斷點就不好使了,在Visual Studio 2019中有一個Debug模式,除錯程式碼時使用Debug模式,打的斷點才能完全發揮作用,否則就只能是告訴開發者出什麼錯了+錯誤在哪行。在vscode裡面除錯預設是的就是這個模式。這個檔案不應該由使用者編輯。

  • MyAppOne.runtimeconfig.dev.json

    一個可有可無的檔案,他的作用是增加附加的執行時探索路徑,dotnet的GitHub的討論上可以看到dotnet官方已經著手取消這個檔案(參見Reason for runtimeconfig.dev.json in build output since .NET Core 3? )。因為大於等於.NET Core 3.0的版本,會將 NuGet 中的庫依賴項複製到輸出資料夾, 而不是在執行時從 NuGet 全域性包資料夾中對其進行解析。也就是說在.NET 5.0這個版本中,runtimeconfig.dev.json這個檔案基本沒有意義。刪除這個檔案不會影響執行。

  • ref資料夾

    引用程式集 是一種特殊型別的程式集,僅包含程式集的公共介面的程式集,它們有助於加快構建過程,因為依賴於該程式集的專案將能夠看到沒有理由重新編譯,即使元件的內部發生了變化,因為從外表上看,它仍然是一樣的。如果想取消ref目錄的生成,在MSBuild檔案中新增<ProduceReferenceAssembly>false</ProduceReferenceAssembly>即可。

dotnet build

生成專案及其所有依賴項,可以類比為編譯。

dotnet build [<PROJECT>|<SOLUTION>] [-a|--arch <ARCHITECTURE>]
    [-c|--configuration <CONFIGURATION>] [-f|--framework <FRAMEWORK>]
    [--force] [--interactive] [--no-dependencies] [--no-incremental]
    [--no-restore] [--nologo] [--no-self-contained] [--os <OS>]
    [-o|--output <OUTPUT_DIRECTORY>] [-r|--runtime <RUNTIME_IDENTIFIER>]
    [--self-contained [true|false]] [--source <SOURCE>]
    [-v|--verbosity <LEVEL>] [--version-suffix <VERSION_SUFFIX>]

dotnet build -h|--help

輸出檔案會寫入到預設位置,即 bin/<configuration>/<target>

常用命令選項

  • -c|--configuration <CONFIGURATION>

    定義生成配置。 大多數專案的預設配置為 Debug,但你可以覆蓋專案中的生成配置設定。也就是說configuration預設是Debug,執行dotnet build之後的輸出檔案會在bin/Debug之下。

  • -f|--framework <FRAMEWORK>

    編譯特定框架。 必須在專案檔案中定義該框架。一般情況下不需要在命令列中指定框架,只要在MSBuild檔案(.csproj)檔案裡面指定一個<TargetFramework>就可以了,如果該專案指定多個框架,在不使用 -f|--framework <FRAMEWORK> 選項指定框架時,執行 dotnet run 將導致錯誤。也就是說,一般情況下,預設的框架是生成專案的.NET SDK的版本,我安裝的是,NET 5.0,那麼我又沒有更改<TargetFramework>,那麼執行dotnet build之後的輸出檔案會在bin/Debug/net5.0之下。

  • -r|--runtime <RUNTIME_IDENTIFIER>

    指定目標執行時。 有關執行時識別符號 (RID) 的列表,請參閱 RID 目錄如果將此選項與 .NET 6.0 SDK 結合使用,則還要使用 --self-contained--no-self-contained。指定了目標執行時之後,.NET 執行時隨應用程式一同釋出。檔案輸出在 bin/<configuration>/<target>/RID之中。常見的RID有win-x64win-x86Linux-x64

Debug vs Release

dotnet rundotnet builddotnet publish中都有一個選項,叫做-c|--configuration <CONFIGURATION>。微軟官方對這個選項的解釋是:

定義生成配置。 大多數專案的預設配置為 Debug,但你可以覆蓋專案中的生成配置設定。

顯然,微軟的官方解釋並不容易理解。其實Debug和Release兩種模式是.NET預設的兩種模式,預設情況下,Debug模式生成的pdb檔案含有全部的斷點資訊,在進行除錯的時候,儲存著除錯和專案狀態資訊、有斷言、堆疊檢查等程式碼。在Visual Studio裡面只有選擇Debug模式才會有完整的除錯資訊,如果選擇Release模式進行除錯,基本上只能輸出出什麼錯了+錯誤在哪行當然這只是.NET的預設設定,可以在Visual Studio裡面進行自定義的配置。在Visual Studio Code裡面,除錯預設使用Debug模式。

Release模式並非一無是處,它編譯時對應用程式的速度進行優化,使得程式在程式碼大小和執行速度上都是最優的

下面提供一些網際網路上關於這兩個模式的討論:

The Release mode enables optimizations and generates without any debug data, so it is fully optimized. . Lots of your code could be completely removed or rewritten in Release mode. The resulting executable will most likely not match up with your written code. Because of this release mode will run faster than debug mode due to the optimizations.


來源:Difference between a Debug and Release build (net-informations.com)

The most important thing is that in Debug mode there are no optimizations, while in Release mode there are optimizations. This is important because the compiler is very advanced and can do some pretty tricky low-level improving of your code. As a result some lines of your code might get left without any instructions at all, or some might get all mixed up. Step-by-step debugging would be impossible. Also, local variables are often optimized in mysterious ways, so Watches and QuickWatches often don't work because the variable is "optimized away". And there are multitudes of other optimizations too. Try debugging optimized .NET code sometime and you'll see.

Another key difference is that because of this the default Release settings don't bother with generating extensive debug symbol information. That's the .PDB file you might have noticed and it allows the debugger to figure out which assembly instructions corresspond to which line of code, etc.


來源:.net - What is the difference between Debug and Release in Visual Studio? - Stack Overflow

建議按照這兩個模式的字面意思來選擇,比如說在除錯階段就用Debug模式,除錯完畢之後的執行、構建和釋出就用Release模式。這樣既能保證在除錯時能夠看到最完整的除錯資訊,也能保證程式碼執行、構建和釋出速度更快。

專案結構

和執行dotnet run之後的專案結構一致,參見dotnet run專案結構

想要保證編譯後的檔案能夠執行MyAppOne.dllMyAppOne.deps.jsonMyAppOne.runtimeconfig.json不可或缺的。如果覺得build之後生成的檔案太多,可以嘗試使用.NET 6.0然後參考單檔案應用程式 - .NET | Microsoft Docs,但是不建議在build階段去嘗試生成單檔案,因為build的本質就是去生成專案及其所有依賴項。如果想要生成單檔案,可以在部署階段(publish)來嘗試單檔案部署。

dotnet publish

將應用程式及其依賴項釋出到資料夾以部署到託管系統。

dotnet publish [<PROJECT>|<SOLUTION>] [-a|--arch <ARCHITECTURE>]
    [-c|--configuration <CONFIGURATION>]
    [-f|--framework <FRAMEWORK>] [--force] [--interactive]
    [--manifest <PATH_TO_MANIFEST_FILE>] [--no-build] [--no-dependencies]
    [--no-restore] [--nologo] [-o|--output <OUTPUT_DIRECTORY>]
    [--os <OS>] [-r|--runtime <RUNTIME_IDENTIFIER>]
    [--self-contained [true|false]]
    [--no-self-contained] [-v|--verbosity <LEVEL>]
    [--version-suffix <VERSION_SUFFIX>]

dotnet publish -h|--help

輸出檔案預設位置是 bin/<configuration>/<target>/publish

常用命令選項

基本上和dotnet build 的常用命令選項一致,有個比較大的不同就是使用-r|--runtime <RUNTIME_IDENTIFIER>時可以指定self-contained,不需要像dotnet build一樣得等到.NET 6.0才能指定self-contained


來源:dotnet publish 命令 - .NET CLI | Microsoft Docs

專案結構

輸出檔案預設位置是 bin/<configuration>/<target>/publish

預設狀態下專案結構和dotnet build專案結構不一樣的點有兩個,一個是沒有了ref資料夾,一個是沒有了runtimeconfig.dev.json

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----        2021/10/17      6:46            416 MyAppOne.deps.json
-a----        2021/10/15     16:01           4608 MyAppOne.dll
-a----        2021/10/15     16:01         125952 MyAppOne.exe
-a----        2021/10/15     16:01           9600 MyAppOne.pdb
-a----        2021/10/17      6:46            147 MyAppOne.runtimeconfig.json

單檔案部署和可執行檔案

單個檔案部署與 Windows 7 不相容。

有兩個好方法:

  1. 修改MSBuild(.csrpoj)檔案,然後dotnet publish

    <Project Sdk="Microsoft.NET.Sdk">
    
      <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net5.0</TargetFramework>
        <PublishSingleFile>true</PublishSingleFile>
        <SelfContained>true</SelfContained>
        <RuntimeIdentifier>win-x64</RuntimeIdentifier>
        <PublishReadyToRun>true</PublishReadyToRun>
      </PropertyGroup>
    
    </Project>
    
  2. 完全通過命令列,無需修改MSBuild(.csrpoj)檔案

    dotnet publish -r linux-x64 -p:PublishSingleFile=true --self-contained true
    

原理解釋

首先,必須指定PublishSingleFile為真,這是生成單個檔案的基礎。SelfContained為真保證可以獨立而不依賴框架,RuntimeIdentifier來指定系統和cpu型別,只有指定了這個,指定SelfContained才有意義

  • --self-contained [true|false]

    .NET 執行時隨應用程式一同釋出,因此無需在目標計算機上安裝執行時。 如果指定了執行時識別符號,並且專案是可執行專案(而不是庫專案),則預設值為 true。 有關詳細資訊,請參閱 .NET 應用程式釋出使用 .NET CLI 釋出 .NET 應用

    如果在未指定 truefalse 的情況下使用此選項,則預設值為 true。 在這種情況下,請不要緊接在 --self-contained 後放置解決方案或專案引數,因為該位置需要 truefalse


來源:dotnet publish 命令 - .NET CLI | Microsoft Docs

即使是釋出單檔案,--self-contained也要根據需求慎重考慮。

dotnet restore

dotnet restore 命令使用 NuGet 還原依賴項以及在 project 檔案中指定的特定於專案的工具。 在大多數情況下,不需要顯式使用 dotnet restore 命令,因為在執行以下命令時,將會在必要時隱式執行 NuGet 還原:

build vs publish

最開始的時候,這倆的區別是輸出的檔案能否在另一臺機器上執行(is ready to be transferred to another machine to run.)。但是隨著.NET框架的發展,build和publish的含義也在發生著變化(其實這要怪微軟,不知道從什麼時候開始,感覺微軟整個公司氛圍開始變成說話不算數,朝令夕改了)。

本文無意去討論在整個漫長的.NET發展週期( .NET Framework.NET Core等 )內這兩個命令含義的變化,現在是2021年10月17日,目前最近的穩定版是.NET 5.0,所以本文只討論.NET 5.0中build和publish的區別。

  • 對於面向 .NET Core 3.0 及更高版本的可執行專案,庫依賴項會被複制到輸出資料夾。 這意味著如果沒有其他任何特定於釋出的邏輯(例如,Web 專案具有的邏輯),則應可部署生成輸出。也就是說.NET 5.0在沒有特殊情況下,build和publish都做好了在另一臺機器上的準備。
  • 相比較於執行dotnet build之後資料夾的變化,dotnet publish會在原有的基礎上生成bin/<configuration>/<target>/publish資料夾,publish資料夾和bin/<configuration>/<target>資料夾相比沒有了ref資料夾和runtimeconfig.dev.json檔案。
  • dotnet publish適用於部署成單檔案。
  • dotnet publish適用於定於生成的檔案是否依賴框架(--self-contained [true|false])

MSBuild

.NET 應用是使用 MSBuild 從原始碼中生成的。 專案檔案(.csproj、.fsproj 或 .vbproj)指定目標和負責編譯、打包和釋出程式碼的關聯任務 。 有引用目標和任務的標準集合的 SDK 識別符號。 使用這些識別符號有助於使專案檔案較小且易於使用。

MSBuild進行修改有兩個方法,一個是在檔案中修改,一個是在命令列中修改。其中在命令列中修改適用於dotnet build -pdotnet publish -p,但不適用於dotnet run。也可以用MSBuild.exe [Switches] [ProjectFile]來進行命令列修改,可以參考MSBuild 命令列參考 - MSBuild | Microsoft Docsdotnet publish 命令 - .NET CLI | Microsoft Docs

dotnet builddotnet publish可以用-p:<NAME>=<VALUE>來定義MSBuild屬性,常用的幾個屬性如下:

  • PublishSingleFile

    啟用單檔案釋出。 此外,還會在 dotnet build 期間啟用單一檔案警告。

  • SelfContained

    確定應用是獨立的還是依賴於框架的。

  • RuntimeIdentifier

    指定目標 OS 和 CPU 型別。 預設情況下,還會設定 <SelfContained>true</SelfContained>

  • PublishReadyToRun

    啟用預先 (AOT) 編譯

注意,.NET 5.0中dotnet build設定SelfContained無效。

關於MSBuild的深度中文解析可以看理解 C# 專案 csproj 檔案格式的本質和編譯流程 - walterlv

.NET CLI 釋出 .NET 應用

.NET 提供了三種釋出應用程式的方法。 依賴於框架的部署會生成一個跨平臺 .dll 檔案,此檔案使用本地安裝的 .NET 執行時。 依賴於框架的可執行檔案會生成一個特定於平臺的可執行檔案,此檔案使用本地安裝的 .NET 執行時。 獨立式可執行檔案會生成一個特定於平臺的可執行檔案,幷包含 .NET 執行時的本地副本。

釋出模式 SDK 版本 命令
依賴框架的部署 2.1 dotnet publish -c Release
3.1 dotnet publish -c Release -p:UseAppHost=false
5.0 dotnet publish -c Release -p:UseAppHost=false
依賴於框架的可執行檔案 3.1 dotnet publish -c Release -r <RID> --self-contained false dotnet publish -c Release
5.0 dotnet publish -c Release -r <RID> --self-contained false dotnet publish -c Release
獨立部署 2.1 dotnet publish -c Release -r <RID> --self-contained true
3.1 dotnet publish -c Release -r <RID> --self-contained true
5.0 dotnet publish -c Release -r <RID> --self-contained true

上述表格來源:使用 .NET CLI 釋出應用 - .NET | Microsoft Docs

另外,還可以通過PublishSingleFile來部署成單個可執行檔案

參考文件

LICENSE

copyright © 2021 蘇月晟,版權所有。

知識共享許可協議
作品蘇月晟採用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。``

相關文章