WHAT IS .NET CLI ?
.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
。
如果在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
初级命令
初级命令就是new
、run
、build
和publish
。
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
。
输出文件会写入到,即 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-x64
、win-x86
和Linux-x64
。
Debug vs Release
在dotnet run
、dotnet build
和dotnet publish
中都有一个选项,叫做-c|--configuration <CONFIGURATION>
。微软官方对这个选项的解释是:
显然,微软的官方解释并不容易理解。其实Debug和Release两种模式是.NET默认的两种模式,默认情况下,Debug模式生成的pdb文件含有全部的断点信息,在进行调试的时候,保存着调试和项目状态信息、有断言、堆栈检查等代码。在Visual Studio里面只有选择Debug模式才会有完整的调试信息,如果选择Release模式进行调试,基本上只能输出出什么错了+错误在哪行。当然这只是.NET的默认设置,可以在Visual Studio里面进行自定义的配置。在Visual Studio Code里面,调试默认使用Debug模式。
Release模式并非一无是处,它编译时对应用程序的速度进行优化,使得程序在代码大小和运行速度上都是最优的。
下面提供一些互联网上关于这两个模式的讨论:
建议按照这两个模式的字面意思来选择,比如说在调试阶段就用Debug模式,调试完毕之后的运行、构建和发布就用Release模式。这样既能保证在调试时能够看到最完整的调试信息,也能保证代码运行、构建和发布速度更快。
项目结构
和运行dotnet run
之后的项目结构一致,参见dotnet run
的项目结构。
想要保证编译后的文件能够运行,MyAppOne.dll
、MyAppOne.deps.json
和MyAppOne.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
。
项目结构
输出文件默认位置是 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 不兼容。
有两个好方法:
修改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>
完全通过命令行,无需修改MSBuild(.csrpoj)文件
dotnet publish -r linux-x64 -p:PublishSingleFile=true --self-contained true
原理解释
首先,必须指定PublishSingleFile为真,这是生成单个文件的基础。SelfContained为真保证可以独立而不依赖框架,RuntimeIdentifier来指定系统和cpu类型,只有指定了这个,指定SelfContained才有意义。
即使是发布单文件,--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]
)
.NET 应用是使用 MSBuild 从源代码中生成的。 项目文件(.csproj、.fsproj 或 .vbproj)指定目标和负责编译、打包和发布代码的关联任务 。 有引用目标和任务的标准集合的 SDK 标识符。 使用这些标识符有助于使项目文件较小且易于使用。
对MSBuild进行修改有两个方法,一个是在文件中修改,一个是在命令行中修改。其中在命令行中修改适用于dotnet build -p
和dotnet publish -p
,但不适用于dotnet run
。也可以用MSBuild.exe [Switches] [ProjectFile]
来进行命令行修改,可以参考MSBuild 命令行参考 - MSBuild | Microsoft Docs和dotnet publish 命令 - .NET CLI | Microsoft Docs。
dotnet build
和dotnet 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 运行时的本地副本。
上述表格来源:使用 .NET CLI 发布应用 - .NET | Microsoft Docs
另外,还可以通过PublishSingleFile来部署成单个可执行文件。
参考文档
- c# - The difference between build and publish in VS? - Stack Overflow
- Debug和Release的区别_不积跬步无以至千里-CSDN博客
- .net core项目结构与发布 - 知乎
- vs 解决方案 - Google 搜索
- 什么是 Visual Studio 解决方案和项目? - Visual Studio (Windows) | Microsoft Docs
- C#:vs封装成dll and 调用生成的dll_SSS_369-CSDN博客
- 使用 Visual Studio Code 创建 .NET 控制台应用程序 - .NET | Microsoft Docs
- 单文件应用程序 - .NET | Microsoft Docs
- .Net 5下的单文件部署 - 天方 - 博客园
- release模式和debug模式有什么区别?有多少种模式? - 知乎
- .net里Release的pdb文件有什么用 是否可以删除 - 天马3798 - 博客园
- .net 5.0 ref文件夹的作用 - dongzhaosheng73 - 博客园
- .net - Visual Studio 中的调试和发布有什么区别? - 堆栈溢出
- .NET SDK 概述 | Microsoft Docs
- dotnet publish 命令 - .NET CLI | Microsoft Docs
- .NET Core 新特性:发布单文件可执行程序 - .NET开发菜鸟 - 博客园
- 深入理解.NET Core的基元: deps.json, runtimeconfig.json, dll文件_dotNET跨平台-CSDN博客
- Visual Studio 调试系列1 Debug 与 Release 模式 - 云+社区 - 腾讯云
LICENSE
copyright © 2021 苏月晟,版权所有。
本作品由苏月晟采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。``