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的使用放在了一起。

下载安装完成之后,安装三个插件,安装方式是点击左侧的插件按钮,进行搜索安装。

.NET CLI简单教程和项目结构-LMLPHP

三个插件如下:

  • 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

输出文件会写入到,即 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和Release两种模式是.NET默认的两种模式,默认情况下,Debug模式生成的pdb文件含有全部的断点信息,在进行调试的时候,保存着调试和项目状态信息、有断言、堆栈检查等代码。在Visual Studio里面只有选择Debug模式才会有完整的调试信息,如果选择Release模式进行调试,基本上只能输出出什么错了+错误在哪行当然这只是.NET的默认设置,可以在Visual Studio里面进行自定义的配置。在Visual Studio Code里面,调试默认使用Debug模式。

Release模式并非一无是处,它编译时对应用程序的速度进行优化,使得程序在代码大小和运行速度上都是最优的

下面提供一些互联网上关于这两个模式的讨论:

建议按照这两个模式的字面意思来选择,比如说在调试阶段就用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

项目结构

输出文件默认位置是 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也要根据需求慎重考虑。

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 -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 运行时的本地副本。

上述表格来源:使用 .NET CLI 发布应用 - .NET | Microsoft Docs

另外,还可以通过PublishSingleFile来部署成单个可执行文件

参考文档

LICENSE

copyright © 2021 苏月晟,版权所有。

.NET CLI简单教程和项目结构-LMLPHP
作品苏月晟采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。``

10-27 04:32