开发套件

.NET SDK = .NET Runtime + .NET CLI

  • 简介
  • 主要内容
    • .NET Runtime
    • .NET CLI 及基础命令
    • MSBuild 命令
    • Nuget 命令
    • 工具管理命令
    • Workload 命令
  • MSBuild 知识点
    • MSBuild 概述
    • 一个“简单”的项目文件
    • 生成过程
    • 发布与部署
  • NuGet 知识
    • 包的生成、上传、引用
    • 依赖解析
  • .NET 工具
    • 工具管理
    • 常用工具与工具库
    • 开发工具
  • 其他知识点
    • 运行时配置
    • global.json
    • .NET卸载工具
    • .NET SDK 遥测

简介

.NET SDK(Software Development Kit,软件开发工具包) 是 .NET 开发的基础组件, SDK 提供了一系列工具来帮助我们在开发过程中完成各种各样的任务,除此之外了解 SDK 中的知识点对于学习 .NET 平台也十分重要。 它包含以下用于构建和运行应用程序的组件:

  • .NET 库和运行时
  • dotnet 驱动程序
  • .NET CLI

从功能的角度来看,.NET SDK 主要包含下面几个方面,

  • 项目相关:创建各类项目,添加和移除项目依赖。
  • 生成相关:将 .NET 项目生成各种用于不同场景部署的程序包,其中包含依赖还原,编译等等功能。
  • Nuget管理:包含各种可以让你生成、上传、管理 Nuget 包的命令。
  • 工具:你可以在 SDK 中安装第三方或自定义的工具,比如:dotnet-ef。

本页目录按照 SDK 中的组件并结合功能来进行编排(也包含除此以外的知识点),如下图所示:



本文的内容主要来自下列的微软官方文档以及开源项目中的文档。

.NET 文档 dotnet/runtime 文档 dotnet/sdk 文档 .NET 设计文档

主要内容

.NET Runtime

.NET Runtime(运行时)是 .NET 托管程序的执行环境,包括 CLR 和 .NET 基础类库 BCL。它是 .NET 平台的基础,也是 .NET SDK 的基础。 在 .NET Runtime 基础之上还有 .NET Desktop Runtime 和 ASP.NET Core Runtime,它们分别为 Windows 桌面程序和 Web 应用程序提供运行环境。

你可以单独安装 .NET Runtime 而不安装 .NET SDK,此时的主机可以运行 .NET 应用程序,但不能使用 SDK 提供的各种功能。

下载 .NET Runtime

在安装 Runtime 后,便可以使用 dotnet 驱动程序 来运行 .NET 应用程序了。命令的最简单形式如下所示,

dotnet myapp.dll 

当然,你也可以在命令中添加额外的参数来配置应用程序运行的方式。带配置参数的命令格式如下所示,

dotnet [--additionalprobingpath <PATH>] [--additional-deps <PATH>]
 [--fx-version <VERSION>]  [--roll-forward <SETTING>]
 <PATH_TO_APPLICATION> [arguments]
dotnet 运行应用参数 说明
--additionalprobingpath <PATH> 该选项指定用于进行探测的探测策略和程序集的路径。
--additional-deps <PATH> 附加 .deps.json 文件的路径。 deps.json 文件包含依赖项、编译依赖项和用于解决程序集冲突的版本信息列表。
--depsfile <PATH_TO_DEPSFILE> deps.json 文件的路径。 .deps.json 文件是一个配置文件,其中包含有关运行应用程序所需的依赖项的信息。 此文件由 .NET SDK 生成。
--runtimeconfig runtimeconfig.template.json 文件的路径。 runtimeconfig.template.json 文件是包含运行时设置的配置文件。
--roll-forward <SETTING> 该选项控制将前滚操作应用于应用的方式。
--fx-version <VERSION> 该选项将重写应用程序 .runtimeconfig.json 文件中第一个框架引用的版本。 这意味着,仅当只有一个框架引用时,它才会按预期方式工作。 如果应用程序具有多个框架引用,则使用此选项可能会导致错误。

除了运行应用之外,dotnet 驱动程序也包含一些参数来显示当前主机的 .NET 环境信息。

dotnet 驱动程序参数 说明
dotnet --info 显示有关 .NET 安装和计算机环境(如当前操作系统)的详细信息,并提交 .NET 版本的 SHA。
dotnet --version 显示 dotnet 命令使用的 .NET SDK 版本。 包括任何 global.json 的影响
dotnet --list-runtimes 显示已安装的 .NET 运行时的列表。 x86 版本的 SDK 只列出 x86 运行时,而 x64 版本的 SDK 只列出 x64 运行时。
dotnet --list-sdks 显示已安装的 .NET SDK 的列表。
dotnet -?|-h|--help 显示可用命令列表。

参考文档
dotnet 命令:https://docs.microsoft.com/zh-cn/dotnet/core/tools/dotnet

.NET CLI 及基础命令

.NET CLI(命令行接口) 是 SDK 功能的交互接口,当我们安装了 .NET SDK 后就可以通过 dotnet 驱动程序来运行这些命令。命令的格式如下所示,

dotnet <命令> [参数] [选项]
下载 .NET SDK

安装 SDK 后,运行 dotnet --help 就会显示主要的 SDK 命令,本节介绍基础的 SDK 命令,除此之外还有MSBuild 命令Nuget 命令工具命令Workload 命令

.NET CLI 基础命令 说明
dotnet build 生成项目。
dotnet build-server 操作 build server 的命令,目前该命令只有 shotdown 子命令来关闭编译服务器。
dotnet clean 该命令可清除上一个生成的输出,中间 (obj) 和最终输出 (bin) 文件夹都会被清除。
dotnet format 设置代码格式以匹配 editorconfig 设置。
dotnet help 显示指定命令更详细的信息。
dotnet migrate 将 .NET Core Preview 2 项目迁移到 .NET Core SDK 样式的项目中。
dotnet new 根据指定的模板创建新的项目、配置文件或解决方案。
dotnet pack 将项目打包为 NuGet 包。
dotnet publish 将应用程序及其依赖项发布到文件夹以部署到目标系统。
dotnet restore 恢复项目的依赖项和工具。
dotnet run 运行项目,无需任何显式编译或启动命令
dotnet sdk check 列出最新可用的 .NET SDK 和 .NET 运行时版本。
dotnet sln 在 .NET 解决方案文件中列出或修改项目。
dotnet store 将指定的程序集存储到运行时包存储区。
dotnet test 用于执行单元测试的 .NET 测试驱动程序。
dotnet add reference 添加项目到项目 (P2P) 引用。
dotnet list reference 列出项目到项目引用。
dotnet remove reference 删除项目到项目 (P2P) 引用。
dotnet add package 向项目文件添加包引用。
dotnet list package 列出项目或解决方案的包引用。
dotnet remove package 从项目文件中删除包引用。
dotnet fsi F# 交互窗口, 在控制台以交互方式运行 F# 代码。

参考文档
.NET CLI 概述:https://docs.microsoft.com/zh-cn/dotnet/core/tools/

MSBuild 命令

MSBuild 是 .NET SDK 生成功能的核心组件,事实上 dotnet build 命令就是使用 MSBuild 来生成项目的,运行 dotnet build 等同于运行 dotnet msbuild -restore,只是输出的默认详细程度不同。 dotnet build 命令除了接受本省定义的选项参数外,也接受 MSBuild 的选项参数,如用来设置属性的 -p 或用来定义记录器的 -l。

下面主要介绍最常用的 dotnet build,关于 MSBuild 的更多知识请查看【MSBuild 知识点】章节。

dotnet build 命令选项 说明
-a|--arch <ARCHITECTURE> 指定目标体系结构。这是用于设置运行时标识符 (RID) 的简写语法,其中提供的值与默认 RID 相结合。例如,在 win-x64 计算机上,指定 --arch x86 会将 RID 设置为 win-x86。 如果使用此选项,请不要使用 -r|--runtime 选项。 该选项从 .NET 6 Preview 7 开始提供。
-c|--configuration <CONFIGURATION> 定义生成配置。 大多数项目的默认配置为 Debug,但你可以覆盖项目中的生成配置设置。
-f|--framework <FRAMEWORK> 编译特定框架。 必须在项目文件中定义该框架。
--force 强制解析所有依赖项,即使上次还原成功,也不例外。
-?|-h|--help 显示有关如何使用命令的说明。
--interactive 允许命令停止并等待用户输入或操作。 例如,完成身份验证。 自 .NET Core 3.0 SDK 起可用。
--no-dependencies 忽略项目到项目 (P2P) 引用,并仅生成指定的根项目。
--no-incremental 将生成标记为对增量生成不安全。 此标记关闭增量编译,并强制完全重新生成项目依赖项关系图。
--no-restore 在生成期间不执行隐式还原。
--nologo 不显示启动版权标志或版权消息。 自 .NET Core 3.0 SDK 起可用。
--no-self-contained 将应用程序发布为框架依赖应用程序。 必须在目标计算机上安装兼容的 .NET 运行时才能运行应用程序。 自 .NET 6 SDK 起可用。
-o|--output <OUTPUT_DIRECTORY> 设置生成二进制文件的目录。 未指定时,默认路径为 ./bin/<configuration>/<framework>/。 对于具有多个目标框架的项目(通过 TargetFrameworks 属性),在指定此选项时还需要定义 --framework。
--os <OS> 指定目标操作系统 (OS)。 这是用于设置运行时标识符 (RID) 的简写语法,其中提供的值与默认 RID 相结合。 例如,在 win-x64 计算机上,指定 --os os 会将 RID 设置为 os-x64。 如果使用此选项,请不要使用 -r|--runtime 选项。 从 .NET 6 Preview 7 开始提供。
-r|--runtime <RUNTIME_IDENTIFIER> 指定目标运行时。 有关运行时标识符 (RID) 的列表,请参阅 RID 目录。 如果将此选项与 .NET 6 SDK 结合使用,则还要使用 --self-contained 或 --no-self-contained。
--self-contained [true|false] .NET 运行时随应用程序一同发布,因此无需在目标计算机上安装运行时。 如果指定了运行时标识符,则默认值为 true。 自 .NET 6 SDK 起可用。
--source <SOURCE> 指定在还原时要使用的 NuGet 包源的 URI。
-v|--verbosity <LEVEL> 设置输出的详细级别。 允许使用的值为 q[uiet]、m[inimal]、n[ormal]、d[etailed] 和 diag[nostic]。 默认值为 minimal。
--version-suffix <VERSION_SUFFIX> 设置生成项目时使用的 $(VersionSuffix) 属性的值。 该设置在未设置 $(Version) 属性时有效。 $(Version) 为 $(VersionPrefix) 与 $(VersionSuffix) 的组合,并用短划线分隔。

dotnet msbuild 命令

dotnet msbuild 命令允许访问功能完备的 MSBuild 命令行工具。

对于 SDK 样式的项目,该命令的功能与 MSBuild 命令行功能完全相同,选项参数也完全一致。 在该文档中你可以查看 MSBuild 命令行的所有选项参数。 也可以使用 dotnet msbuild -h 命令来进行快速的查看。

参考文档
dotnet build 命令参考:https://docs.microsoft.com/zh-cn/dotnet/core/tools/dotnet-build
dotnet msbuild 参考:https://docs.microsoft.com/zh-cn/dotnet/core/tools/dotnet-msbuild
MSBuild 命令行参考:https://docs.microsoft.com/zh-cn/visualstudio/msbuild/msbuild-command-line-reference

Nuget 命令

Nuget 是微软的包分享平台,.NET 平台使用该平台来创建、存储和分享包,这些包除了可以存储在官方的仓库 nuget.org 之外,也可以存储在你建立的私有 nuget 仓库中。

生成 nuget 包的命令是基础命令里的 dotnet pack 命令,本节介绍的 nuget 命令是对 nuget 仓库的操作命令,包括上传,删除,更新包以及对 nuget 源的操作。

Nuget 包仓库管理命令 说明
dotnet nuget delete 从 nuget 仓库中删除或取消列出包
dotnet nuget locals 清除或列出本地 NuGet 资源。
dotnet nuget push 将包推送到服务器,并将其发布。
dotnet nuget add source 添加 NuGet 源。
dotnet nuget disable source 禁用 NuGet 源
dotnet nuget enable source 启用 NuGet 源。
dotnet nuget list source 列出所有配置的 NuGet 源。
dotnet nuget remove source 删除 NuGet 源。
dotnet nuget update source 更新 NuGet 源。
dotnet nuget verify 验证已签名的 NuGet 包。
dotnet nuget trust 获取受信任的签名者或将受信任的签名者设置为 NuGet 配置。
dotnet nuget sign 使用证书对匹配第一个参数的所有 NuGet 包进行签名。

参考资料
dotnet nuget 命令:https://docs.microsoft.com/zh-cn/dotnet/core/tools/dotnet-nuget-locals
nuget Github:https://github.com/NuGet
nuget packages:https://www.nuget.org/packages

工具管理命令

.NET 工具是一种特殊的 NuGet 包,其中包含控制台应用程序。在 CLI 中安装工具后你就可以以命令行的形式运行工具。 常用的工具如:dotnet-ef 可以帮助我们完成一系列 EntityFramework Core 相关的开发任务。

更多内容可查看【.NET 工具】章节。

dotnet tool 管理命令 说明
dotnet tool install 安装指定的 .NET 工具。
dotnet tool list 列出当前安装的所有指定类型的 .NET 工具。
dotnet tool restore 安装当前目录范围内的 .NET 本地工具。
dotnet tool run 运行本地工具。
dotnet tool search 搜索已发布到 NuGet 的 .NET 工具。
dotnet tool uninstall 卸载指定的 .NET 工具。
dotnet tool update 更新指定的 .NET 工具。

参考资料
如何管理 .NET 工具:https://docs.microsoft.com/zh-cn/dotnet/core/tools/global-tools
dotnet tool 命令参考:https://docs.microsoft.com/zh-cn/dotnet/core/tools/dotnet-tool-install

Workload 命令

什么是 Workload(工作负载)?

在 .NET SDK 的上下文中,Workload 是 .NET SDK 支持开发的各种应用类型,比如:Console 是一种 Workload,ASP.NET Core 是一种 Workload,WPF 是一种 Workload,目前这些 Workload 都已包含在 SDK 中。 但是随着 SDK 能开发的应用类型越来越多,例如:MAUI 和 Blazor,因为不可能无限制的将这些 Workload 内建到 SDK 中,所以为了控制 SDK 的体积,.NET 6 在 SDK 中加入了 Workload 功能,让开发者可以根据自己的需要安装 Workload。

查看可安装的 Workload 清单,可以使用命令 dotnet workload search 进行查看,目前(2021年12月17日)这个列表如下:

Workload ID 说明
android .NET SDK Workload for building Android applications.
android-aot .NET SDK Workload for building Android applications with AOT support.
ios .NET SDK Workload for building iOS applications.
maccatalyst .NET SDK Workload for building macOS applications with MacCatalyst.
macos .NET SDK Workload for building macOS applications.
maui .NET MAUI SDK for all platforms
maui-android .NET MAUI SDK for Android
maui-desktop .NET MAUI SDK for Desktop
maui-ios .NET MAUI SDK for iOS
maui-maccatalyst .NET MAUI SDK for Mac Catalyst
maui-mobile .NET MAUI SDK for Mobile
maui-windows .NET MAUI SDK for Windows
tvos .NET SDK Workload for building tvOS applications.
wasm-tools .NET WebAssembly 生成工具

Workload 的操作命令如下表所示:

dotnet workload 管理命令 说明
dotnet workload install 安装可选的工作负载。
dotnet workload list 列出已安装的工作负载。
dotnet workload repair 修复工作负载安装。
dotnet workload restore 安装项目或解决方案所需的工作负载。
dotnet workload search 搜索可选工作负载。
dotnet workload uninstall 卸载指定的工作负载。
dotnet workload update 更新已安装的工作负载。

参考资料
.NET SDK 工作负载:https://github.com/dotnet/designs/blob/main/accepted/2020/workloads/workloads.md

MSBuild 知识点

MSBuild 概述

MSBuild 全称为 Microsoft Build Engine,是一个用于构建(生成)应用程序的平台。MSBuild 引擎提供了一个 xml 格式的项目文件,用于控制平台如何构建应用程序。 在 .NET 中这个文件就是 .csproj、.vbproj、.fsproj 这些项目文件,这些项目文件在组织项目文件的同时也作为 MSBuild 的输入用于生成项目

我们可以通过多种方式使用 MSBuild, 比如 Visula Studio 使用 MSBuild,可以单独下载 MSBuild 命令行工具, 以及在 .NET CLI 中使用 dotnet build 和 dotnet msbuild 命令,dotnet build 等同于运行 dotnet msbuild -restore 命令,并且 dotnet cli 中很多命令都会触发生成操作。

以下命令都会触发生成操作,并且都能接受与 dotnet build 相似的生成配置选项。

生成与编译

虽然 MSBuild 的功能是生成应用程序但它本身不包含编译功能,它调用语言对应的编译器来完成编译操作,对于C#和VB 是 Roslyn,F# 则使用自己的编译器 fsc。 由此可以看出 MSBuild 偏向于组织项目文件和打包输出的功能。

重要概念!!!

要了解 MSBuild 的工作机制,首先要两个四个重要概念:属性(properties),项(items),任务(tasks)和目标(targets)

属性:属性是用于配置生成的键/值对,通常在项目文件中定义,也可以在命令行中定义。 在项目文件中,属性为 <PropertyGroup> 元素的子元素。

项:项是生成引擎的输入,表示文件或包。在项目文件中,项为 <ItemGroup> 元素的子元素。

任务:MSBuild 能够在生成过程中执行任意数量的操作,而任务就是由 MSBuild 用于执行原子生成操作的可执行代码单元,MSBuild 使用任务来执行这些操作。 比如创建目录(MakeDir)是一个任务、拷贝文件(Copy)是一个任务、编译(Csc)是一个任务。MSBuild 提供的任务都定义在命名空间 Microsoft.Build.Tasks 中, 除此之外你也可以创建并使用自己定义的任务。

目标:目标是一系列任务的有序集合。你可以在 C:\Program Files\dotnet\sdk\6.0.100 目录下找到一系列的 targets 文件, 这些 targets 定义了具体生成步骤。

关键属性

目标框架与TFM:目标框架指的是项目所使用的框架,该配置存在于项目文件的属性 <TargetFramework> 中。如下所示的代码含义为:该项目所使用的框架为 .NET 6。

<PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
</PropertyGroup>

而 TFM(Target Framewoke Moniker)目标框架别名,指的是上面所说的 net6.0 这个值,这个值就是 .NET 6 这个框架的 TFM。 .NET 平台定义了一系列这样的框架别名用于在定义中使用,部分目标框架及 TFM 如下表所示:

.NET Core 以及 .NET 5+ 目标框架 目标框架别名 / TFM
.NET Core 2.0 netcoreapp2.0
.NET Core 2.1 netcoreapp2.1
.NET Core 3.0 netcoreapp3.0
.NET Core 3.1 netcoreapp3.1
.NET 5 net5.0
.NET 6 net6.0
.NET Framework 目标框架 目标框架别名 / TFM
.NET Framework 2.0 net20
.NET Framework 3.5 net35
.NET Framework 4.0 net40
.NET Framework 4.5 net45
.NET Framework 4.6 net46
.NET Framework 4.7 net47
.NET Framework 4.7.2 net472
.NET Framework 4.8 net48

全部 TFM 的表格请查看目标框架文档。

运行时标识符 / RID(Runtime Identifier): RID 值用于标识应用程序运行的目标平台,RID 主要包含操作系统和CPU架构两部分信息。通过在项目文件中设置属性 <RuntimeIdentifier> 来指定项目的目标平台,在跨平台应用程序的项目文件中省略此设置。

表示具体操作系统的 RID 通常遵循以下模式:[os].[version]-[architecture]-[additional qualifiers],其中:

  • [os] 是操作系统/平台系统名字对象。 例如 ubuntu。
  • [version] 是操作系统版本,使用的格式是以点 (.) 分隔的版本号。 例如 15.10。
  • [architecture] 是处理器体系结构。 例如:x86、x64、arm 或 arm64。
  • [additional qualifiers] 进一步区分了不同的平台。 例如:aot。

下表列出了部分常见的 RID,.NET 支持的全部 RID 请参考 runtime.json RID 目录

操作系统 RID
Windows RID win-arm64
win7-x86
win81-x64
win10-arm64
win10-x64
...
Linux RID linux-x64
linux-musl-x64
rhel-x64
tizen.5.0.0
...
macOS RID osx-x64
osx.10.12-x64
osx.10.14-x64
osx.11.0-x64
osx.11.0-arm64
...

参考资料
MSBuild概念:https://docs.microsoft.com/zh-cn/visualstudio/msbuild/msbuild-concepts
MSBuild 任务参考:https://docs.microsoft.com/zh-cn/visualstudio/msbuild/msbuild-task-reference
自定义任务:https://docs.microsoft.com/zh-cn/visualstudio/msbuild/msbuild-tasks
MSBuild 文档:https://docs.microsoft.com/zh-cn/visualstudio/msbuild/msbuild
创建 MSBuild 项目文件:https://docs.microsoft.com/zh-cn/visualstudio/msbuild/walkthrough-creating-an-msbuild-project-file-from-scratch
MSBuild 参考:https://docs.microsoft.com/zh-cn/visualstudio/msbuild/msbuild-reference

一个“简单”的项目文件

MSBuild 使用项目文件指示生成引擎要生成哪些内容以及生成方法。另外,这种项目文件格式还允许开发人员创建可重用的生成规则,这些规则可以被包含到不同的文件中以方便重用。

这里以一个简单的 C# Console 项目文件为例来介绍属性和项。这是一个最简单的 Conole 项目,然后引用了一个 System.Drawing.Common Nuget 包。

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

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

    <ItemGroup>
        <PackageReference Include="System.Drawing.Common" Version="6.0.0" />
    </ItemGroup>

</Project>

Project 元素是 MSBuild 项目文件必需的根元素,其中的 Sdk Attribute 定义了项目使用的 Sdk, 除了 Microsoft.NET.Sdk 外 .NET 项目的 Sdk 还有 Microsoft.NET.Sdk.Web 以及 Microsoft.NET.Sdk.WindowsDesktop 等。 更多关于 SDK 的内容请查阅项目 SDK 文档

PropertyGroup 元素的子元素就是 MSBuild 概念中的属性,属性提供键值对来配置 MSBuild 的生成。

ItemGroup 元素的子元素就是 MSBuild 概念中的,项定义了引擎的输入,包括文件、Nuget包引用等。

这算是一个最简单的项目文件了,深入了解项目文件的知识可以查阅参考资料,关于项目文件在生成过程中的处理可查看下一节【生成过程】。

参考资料
.NET 项目 SDK 概述:https://docs.microsoft.com/zh-cn/dotnet/core/project-sdk/overview
MSBuild 文档:https://docs.microsoft.com/zh-cn/visualstudio/msbuild/msbuild
创建 MSBuild 项目文件:https://docs.microsoft.com/zh-cn/visualstudio/msbuild/walkthrough-creating-an-msbuild-project-file-from-scratch
MSBuild 参考:https://docs.microsoft.com/zh-cn/visualstudio/msbuild/msbuild-reference

生成过程

完整的生成过程由启动(Startup)、评估(Evaluation)和执行(Execution)组成。

启动

  • 启动方式,可以通过 Visual Sutdio、脚本以及.NET CLI 启动 MSBuild。
  • 输入文件,输入文件通常是项目文件,在使用 Visual Studio 的情况下则可能是解决方案文件(sln)。MSBuild 处理两种输入文件的方式会有不同,详细内容可查看参考资料。
  • 标准导入,Microsoft.Common.props 和 Microsoft.Common.targets 都是由 .NET 项目文件导入的(在 SDK 样式项目中显式或隐式导入), 并且实际上 Microsoft.Common.targets 是一个导入 Microsoft.Common.CurrentVersion.targets 的精简包装器,后者包含了定义生成过程的主要内容。这些文件都在 dotnet sdk 目录下可以找到。

评估

  • 评估环境变量,该阶段环境变量用于设置等效属性。 例如,PATH 环境变量作为属性 $(PATH)。 从命令行或脚本运行时,将正常使用命令环境;从 Visual Studio 运行时,将使用 Visual Studio 启动时生效的环境。
  • 评估导入和属性,该阶段将读入整个输入 XML,包括项目文件和整个导入链,并创建一个内存中 XML 结构。
  • 评估项定义,该阶段将解析项定义,并在内存中创建这些定义的实例。
  • 评估项, 该阶段将处理任何目标之外的项及其关联的元数据。
  • 评估 UsingTask 元素,该阶段将读取 UsingTask 元素,并声明这些任务以供稍后在执行阶段使用。
  • 评估目标,该阶段将在内存中创建所有目标对象结构,以准备执行,无实际执行发生。

执行

在执行阶段,所有定义和导入的目标会被排序并依次执行(目标执行的顺序可通过目标元素中的 BeforeTargets,DependsOnTargets 和 AfterTargets 这些 Attribute 来定义)。

参考资料
MSBuild 如何生成项目:https://docs.microsoft.com/zh-cn/visualstudio/msbuild/build-process-overview
MSBuild 任务参考:https://docs.microsoft.com/zh-cn/visualstudio/msbuild/msbuild-task-reference

发布与部署

发布应用程序使用 dotnet publish 命令,该命令会调用 MSBuild 此时 MSBuild 会调用 Publish 对应的目标文件,其所在的 targets 文件都在 dotnet sdk 的 Microsoft.NET.Sdk.Publish\targets 目录下。 如果 IsPublishable 属性被设置为 false 则不会调用 publish 目标。

开发者在生成项目的时候,可能需要针对不停的操作系统不同的CPU架构生成不同的程序包, 还要考虑目标平台上是否安装了 .NET Runtime 或者生成程序包的体积甚至是否要生成单文件程序包。针对这些不同的场景需要配置不同的生成过程。

发布过程中,有几个重要的配置选项让我们可以配置发布包:

是否跨平台?

在发布时可以指定 runtime 来生成特定于目标平台的应用程序,如下面的代码所示,关于 -r|--runtime 选项的值请查看文档:RID 目录。

dotnet publish #默认情况下发布的应用程序是跨平台的
dotnet publish -r win-x64 #发布到windows x64架构应用程序
dotnet publish -r win-arm #发布到windows ARM架构应用程序
dotnet publish -r linux-x64 #发布到linux x64架构应用程序

是否依赖于框架?

  • 依赖于框架(framework-dependent),生成的应用程序将仅包含该应用程序本身及其依赖项,应用程序需要在安装 .NET 运行时的计算机上运行。
    dotnet publish #默认情况生成依赖于框架的应用程序
  • 独立部署(自包含/self-contained),生成的应用程序将包含 .NET 运行时和库,所以应用程序可以在未安装 .NET 运行时的计算机上运行。该方式的缺点是应用程序的体积会变大约66MB(不同版本的目标框架会有所不同)。
    #发布独立部署必须指定 -r|--runtime 并设置 --self-contained 为 true
    dotnet publish -r win-x64 --self-contained true

是否剪裁部署发布包?

剪裁发布包只针对【独立部署】,剪裁部署会移除不被使用的程序集以减少发布包的体积。某些情况(主要是反射的使用)会导致剪裁无法准确分析需要剪裁的代码而导致应用程序运行失败, 所以剪裁发布后的应用程序需要全面的测试。仅 .NET 6 及更高版本支持剪裁。

#当设置 PublishTrimmed 为 true 时会自动启用独立部署方式
dotnet publish -r win-x64 -p:PublishTrimmed=true

是否 ReadyToRun?

可以通过将应用程序集编译为 ReadyToRun (R2R) 格式来改进 .NET 应用程序的启动时间和延迟。 R2R 是一种预先编译(AOT/Ahead of Time)形式。

R2R 的二进制文件通过减少应用程序加载时实时 (JIT) 编译器需要执行的工作量来改进启动性能。 二进制文件包含了与 JIT 生成内容类似的本机代码。 但是,R2R 二进制文件会更大,因为它包含中间语言 (IL) 代码(某些情况下仍需要此代码)和其对应的本机代码。 仅当发布面向特定运行时环境 (RID)(如 Linux x64 或 Windows x64)的应用时 R2R 才可用。

#将属性 PublishReadyToRun 便可启用 R2R 生成
dotnet publish -c Release -r win-x64 -p:PublishReadyToRun=true

你也可以在项目文件中设置属性以达到相同的效果

<PropertyGroup>
  <PublishReadyToRun>true</PublishReadyToRun>
</PropertyGroup>

是否单文件部署?

通过将所有依赖应用程序的文件捆绑到一个二进制文件中,为应用程序开发人员提供一个具有吸引力的选项,那就是将应用程序作为单个文件进行部署和分发。 此部署模型从 .NET Core 3.0 开始提供,在 .NET 5 中进行了增强。 之前在 .NET Core 3.0 中,当用户运行单文件应用时,.NET Core 主机会先将所有文件提取到一个目录,然后再运行该应用程序。 .NET 5 改进了这一体验,它可直接运行代码,无需从应用中提取文件。

单文件部署可用于【依赖框架部署】和【独立部署】的应用程序。 独立部署应用程序中单个文件的体积很大,因为它包含运行时和框架库。 单文件部署选项可与 ReadyToRun 和 Trim 发布选项结合使用。

#控制是否单文件部署的属性为 PublishSingleFile
dotnet publish -r linux-x64 -p:PublishSingleFile=true --self-contained false

参考资料
.NET 部署模型:https://docs.microsoft.com/zh-cn/dotnet/core/deploying/
TFM:https://docs.microsoft.com/zh-cn/dotnet/standard/frameworks
特定OS的 TFM:https://docs.microsoft.com/zh-cn/dotnet/standard/frameworks#net-5-os-specific-tfms
.NET RID 目录:https://docs.microsoft.com/zh-cn/dotnet/core/rid-catalog

NuGet 知识

包的生成、上传、引用

NuGet 是适用于 .NET 的包管理器。 它使开发人员能够创建、共享和使用有用的 .NET 库。

生成

使用 dotnet pack 命令可将项目打包为后缀为 .nupkg 的 nuget 包,

dotnet pack 

创建包需要提供以下属性,

<PropertyGroup>
    <PackageId>AppLogger</PackageId>
    <Version>1.0.0</Version>
    <Authors>your_name</Authors>
    <Company>your_company</Company>
</PropertyGroup>

关于这些 nuget 属性的详情以及更多的 nuget 属性,可参考该文档

上传

开发者可以将自己的 nuget 包发布到在线 nuget 仓库以分享自己的包,通常第三方库也是通过这种方式发布自己的 .NET 包。

这里以 nuget.org 官方仓库为例演示如何向仓库上传包,首先你需要在 nuget.org 网站是注册一个账号并获取 API 密钥,并确定一个唯一的 PackageId,如 Newtonsoft.Json。 这样就可以上传 nuget 包了。

更多关于 push 的命令可查看该文档

dotnet nuget push demo.nupkg -k 4003d786cc374004bfdfc4f3e8ef9b3a

具体的 nuget 包上传操作可查阅这篇文章

引用

引用一个 nuget 包是非常简单的,特别是在 VisualStudio 中,提供了非常好用的工具来管理项目的 nuget 引用。在 .NET CLI 中也可以很简单的通过命令来查找和安装 nuget 包。

#为当前项目添加 nuget 包引用命令
dotnet add package Newtonsoft.Json

该命令会修改项目文件并在ItemGroup元素下添加 PackageReference 项。

<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />

另外,使用 dotnet list package 命令可查看当前项目引用的包。

参考资料
NuGet 文档:https://docs.microsoft.com/zh-cn/nuget/
打包输入属性:https://docs.microsoft.com/zh-cn/nuget/reference/msbuild-targets#pack-target
dotnet pack 命令:https://docs.microsoft.com/zh-cn/dotnet/core/tools/dotnet-pack
第三方 nuget 仓库:https://docs.microsoft.com/zh-cn/nuget/hosting-packages/overview
创建自己的 nuget 仓库:https://docs.microsoft.com/zh-cn/nuget/hosting-packages/nuget-server

依赖解析

为一个项目添加依赖包的时候,其实本质上就是在项目文件中添加了一个如下的项:

<ItemGroup>
        <PackageReference Include="System.Drawing.Common" Version="6.0.0" />
</ItemGroup>

之后当包开始安装或者当项目开始还原或生成的时候,nuget 会根据项目中引用的包来解析出整个“依赖关系图(dependency graph)” 并还原所有关系图中的依赖包。 关于依赖的解析规则可查看该文档

在完成整个解析过程后,nuget 会将生成的依赖关系图写入 [appname].deps.json 文件。该文件包含了依赖的清单,同时也包含了编译上下文数据和编译依赖, 该文件并不是必要的,但是在需要使用服务或包缓存/共享包安装功能时是必须的。并且该文件设计上是由工具自动处理的不应手动进行修改。

参考资料
NuGet 如何解析包依赖项:https://docs.microsoft.com/zh-cn/nuget/concepts/dependency-resolution
NuGet 文件夹结构:https://docs.microsoft.com/zh-cn/nuget/create-packages/supporting-multiple-target-frameworks

.NET 工具

.NET 工具是一种特殊的 NuGet 包,其中包含控制台应用程序。在 CLI 中安装工具后你就可以以命令行的形式运行工具。 常用的工具如:dotnet-ef 可以帮助我们完成一系列 EntityFramework Core 相关的开发任务。

工具管理

工具包含两大类:全局工具和本地工具。

安装全局工具

全局工具指的是安装在全局目录中的工具,全局工具可以在任何位置执行。安装全局工具需要提供 --global 参数,默认情况下安装工具为本地工具。

dotnet tool install dotnet-cf --global#安装 dotnet-cf 为全局工具

全局工具安装路径

OS 全局工具路径
Linux/macOS $HOME/.dotnet/tools
Windows %USERPROFILE%\.dotnet\tools

安装本地工具

本地工具指的是仅能在本地访问的工具(“本地”就是当前目录或子目录),要安装本地工具首先要在目录中添加工具清单文件(tool-manifest)。

dotnet new tool-manifest #添加工具清单文件

运行该命令后目录中会多出一个 .config 文件夹 里面包含一个 dotnet-tools.json 文件,该文件记录了安装的本地工具。

dotnet tool install dotnet-cf #安装 dotnet-cf 为本地工具
dotnet tool install --local dotnet-cf #安装 dotnet-cf 为本地工具,显式指定

运行工具命令

如果命令的前缀为 dotnet- 则可以用下面的方式调用命令:

dotnet tool run dotnet-doc
dotnet dotnet-doc
dotnet doc
dotnet-doc #全局工具可以直接使用工具命令名称调用

其他情况则可以用下面的方式调用命令:

dotnet tool run dotnetsay
dotnet dotnetsay
dotnetsay#全局工具可以直接使用工具命令名称调用

全局工具直接使用工具命令调用的前提为【全局工具路径】在环境变量 PATH 中。

卸载工具

使用以下命令可以卸载工具

dotnet tool uninstall --global <packagename> #卸载全局工具
dotnet tool uninstall --tool-path <packagename> #卸载指定目录工具
dotnet tool uninstall <packagename> #卸载本地工具

参考资料
如何管理 .NET 工具:https://docs.microsoft.com/zh-cn/dotnet/core/tools/global-tools

常用工具与工具库

常用工具

工具名 命令 说明
dotnet-ef dotnet-ef Entity Framework Core 工具
microsoft.dotnet-interactive dotnet-interactive .NET Interactive,使开发者使用交互的方式运行代码
gitversion.tool dotnet-gitversion GitVersion 的 dotnet 工具版

诊断工具

工具名 说明
dotnet-counters 性能计数器
dotnet-coverage 代码覆盖率实用工具
dotnet-dump 转储收集和分析实用工具
dotnet-gcdump 堆分析工具
dotnet-trace 性能分析实用工具
dotnet-stack 检查托管堆栈跟踪
dotnet-symbol 符号下载器
dotnet-sos dotnet-sos 全局工具将安装 SOS 调试程序扩展。 借助此扩展,你可以从本机调试器(如 lldb 和 windbg)检查托管 .NET Core 状态。
dotnet-dsrouter dotnet-dsrouter 将 dotnet-trace 和 dotnet-counters 等诊断工具连接到在 Android、iOS 和 tvOS 上运行的 .NET 应用程序,无论它们是作为仿真器、模拟器还是在设备本身上运行。 诊断工具使用本地进程间通信 (IPC)(命名管道、Unix 域套接字)来连接 .NET 运行时并与之通信。

其他工具

更多工具可以前往 NuGet 工具页 查找所需的工具,目前收录了将近3000个工具(2021年12月22日)。

开发工具

.NET SDK 工具的本质是一个控制台程序,所以开发自己的工具非常简单。

首先创建一个控制台项目,开发你需要的功能,然后打包成 nuget 包。打包时需要注意指定 <PackAsTool> 和 <ToolCommandName> , 通过在项目文件的 PropertyGroup 节点中添加如下所示的属性:

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

  <PropertyGroup>

    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>

    <PackageId>MyTool</PackageId>
    <PackAsTool>true</PackAsTool>
    <ToolCommandName>dotnet-my</ToolCommandName>
    <PackageOutputPath>./nupkg</PackageOutputPath>

  </PropertyGroup>

</Project>

之后你可以选择将工具 nuget 包上传到 nuget 仓库,或者选择从本地安装工具。命令如下所示:

dotnet tool install -g MyTool #从 nuget 源中安装MyTool为全局工具
dotnet tool install -g --add-source [工具nupkg包所在目录] MyTool #离线安装MyTool

这里需要注意的是若工具命令名称(ToolCommandName)以 dotnet- 为前缀,调用方式如下:

dotnet tool run dotnet-my
dotnet dotnet-my
dotnet my
dotnet-my #全局工具可以直接使用工具命令名称调用

#若不是dotnet- 前缀,比如命令名为 mytool 则通过下面的方式调用
dotnet mytool
dotnet tool run mytool
mytool#全局工具可以直接使用工具命令名称调用

参考资料
创建工具:https://docs.microsoft.com/zh-cn/dotnet/core/tools/global-tools-how-to-create

其他知识点

运行时配置

.NET 提供了下面三种机制来配置应用程序的运行时行为(某些配置选型可能只适用于其中一种配置机制):

  • runtimeconfig.json,该文件的前缀是应用程序名称,如 myapp.runtimeconfig.json,因此文件中的配置也仅对该应用起作用。
  • MSBuild 属性,在 MSBuild 属性(既项目文件中 PropertyGroup 下的属性)中设置的运行时配置会在生成时写入 *.runtime.json 文件。
  • 环境变量,在环境变量中设置的运行时配置会应用于本机上所有的 .NET 应用程序。

本节列出了所有可配置项。但是由于配置的具体内容过多不适合在本页展示,所以要了解配置的详细信息请点击配置名称链接进行查阅。

编译设置,文档

配置名称 说明
分层编译 配置实时 (JIT) 编译器是否使用分层编译。.NET Core 3.0+ 默认启用;在 .NET Core 2.1 和 2.2 中 默认禁用
快速 JIT 配置 JIT 编译器是否使用快速 JIT。.NET Core 3.0+ 默认启用;在 .NET Core 2.1 和 2.2 中 默认禁用
用于循环的快速 JIT 配置 JIT 编译器是否对包含循环的方法使用快速 JIT。默认禁用。
ReadyToRun 配置 .NET Core 运行时是否要为具有可用 ReadyToRun 数据的映像使用预编译代码。默认启用。
按配置优化 此设置在 .NET 6 及更高版本中 (PGO) 启用动态或分层按配置优化。

调试和分析设置,文档

配置名称 说明
启用诊断 配置是启用还是禁用调试器、探查器和 EventPipe 诊断。如果省略此设置,则会启用诊断。
启用分析 配置是否为当前正在运行的进程启用分析。如果省略此设置,则会禁用分析。
探查器 GUID 指定要加载到当前正在运行的进程中的探查器 GUID。
探查器位置 指定要加载到当前正在运行的进程(或 32 位/64 位进程)的探查器 DLL 路径。
写入 Perf 映射 允许或禁止在 Linux 系统上写入 /tmp/perf-$pid.map。如果省略此设置,则会禁止写入 Perf 映射。
性能日志标记 允许或禁止在性能日志中将指定信号作为标记予以接受和忽略。如果省略此设置,则不会忽略指定的信号。

垃圾回收设置,文档

配置名称 说明
垃圾回收风格 配置应用程序是使用工作站垃圾回收还是服务器垃圾回收。关于两种垃圾回收设置,请参考文档工作站和服务器垃圾回收
后台垃圾回收 配置是否启用后台(并发)垃圾回收。默认:使用后台垃圾回收。
堆计数 限制通过垃圾回收器创建的堆数。
关联掩码 指定垃圾回收器线程应使用的确切处理器数。
关联范围 指定用于垃圾回收器线程的处理器列表。
GC 是否使用 CPU 组 配置垃圾回收器是否使用 CPU 组。默认:垃圾回收不会跨 CPU 组扩展。
关联 指定是否将垃圾回收线程与处理器关联。默认:将垃圾回收线程与处理器关联。
堆限制 指定 GC 堆和 GC 簿记的最大提交大小(以字节为单位)。默认值(仅在某些情况下适用)是 20 MB 或容器内存限制的 75%(以较大者为准)。
堆限制百分比 指定允许的 GC 堆使用量占总物理内存的百分比。(百分比)
每对象堆限制 可以根据每个对象堆指定 GC 的允许堆使用量。(以字节为单位)
每对象堆限制百分比 可以根据每个对象堆指定 GC 的允许堆使用量。(百分比)
高内存百分比 内存负载由正在使用的物理内存的百分比表示。 默认情况下,当物理内存负载达到 90%时,垃圾回收对于执行完整的压缩垃圾回收变得更加积极,以避免分页。 当内存负载低于 90% 时,GC 优先使用后台回收进行完整的垃圾回收,这种方法的暂停时间较短,但不会使堆的总大小减少太多。
保留 VM 配置是将应删除的段置于备用列表上供将来使用,还是将其释放回操作系统 (OS)。默认:将段释放回操作系统。
大型页面 指定设置堆硬限制时是否应使用大型页面。默认:设置堆硬限制时不要使用大页面。
允许大型对象 在 64 位平台上,为总大小大于 2 千兆字节 (GB) 的数组配置垃圾回收器支持。默认:垃圾回收支持大于 2 GB 的数组。
大型对象堆阈值 指定导致对象进入大型对象堆 (LOH) 的阈值大小(以字节为单位)。默认阈值为 85,000 字节。
独立 GC 指定库的路径,该库包含运行时打算加载的垃圾回收器。

全球化设置,文档

配置名称 说明
固定模式 确定 .NET Core 应用是否以全球化固定模式运行而无权访问特定区域性的数据和行为。如果省略此设置,应用可运行并可访问区域性数据,即 false。
纪元年份范围 确定是否放宽对支持多个纪元的日历的范围检查,或者超出纪元日期范围的日期是否引发 ArgumentOutOfRangeException。默认:会放宽范围检查。
日语日期分析 确定是否成功分析包含“1”或“Gannen”作为年份的字符串,或是否仅支持“1”。默认值为 false。
日语年份格式 确定是将日本历时代的第一年的格式设置为“Gannen”还是设置为一个数字。默认:第一年的格式为“Gannen”
NLS 确定 .NET 是否使用适用于 Windows 应用的 Unicode (ICU) 全球化 API 的区域语言支持 (NLS) 或国际组件。默认情况下,.NET 使用 ICU 全球化 API。
预定义的区域性 配置应用在启用全球化固定模式时,是否可以创建除固定区域性之外的区域性。默认:NET 会限制全球化固定模式下的区域性创建。

网络设置,文档

配置名称 说明
HTTP/2 协议 配置是否启用对 HTTP/2 协议的支持。 .NET Core 3.0 默认禁用;.NET Core 3.1 和 .NET 5+ 默认启用。
HttpClient 中的 SPN 创建 当 Host 标头缺失并且目标未在默认端口上运行时,会影响用于 Kerberos 和 NTLM 身份验证的服务主体名称 (SPN) 的生成。.NET Core 2.x 和 3.x 不在 SPN 中包括端口。.NET Core 5.x 在 SPN 中包括端口。.NET 6 及更高版本不包括端口,但行为是可配置的。
UseSocketsHttpHandler 配置 System.Net.Http.HttpClientHandler 是使用 System.Net.Http.SocketsHttpHandler 还是使用旧的 HTTP 协议堆栈。默认:使用 SocketsHttpHandler。
Latin1 标头 配置 SocketsHttpHandler 是否可以在标头中发送 ISO-8859-1 (Latin-1) 编码的字符。默认:不可以。此选项仅在 3.1.9 版本之后 NET Core 3.1 中提供,先前版本或更高版本中则不提供。

线程设置,文档

配置名称 说明
CPU 组 配置是否在各 CPU 组之间自动分布线程。默认:不会跨 CPU 组分布线程。
最小线程数 指定工作线程池的最小线程数。对应于 ThreadPool.SetMinThreads 方法。
最大线程数 指定工作线程池的最大线程数。对应于 ThreadPool.SetMaxThreads 方法。
响应阻塞工作项的线程注入 在某些情况下,线程池会检测阻止其线程的工作项。 为了进行补偿,会注入更多线程。 在 .NET 6+ 中,可以使用以下运行时配置设置来配置线程注入,以响应阻止工作项。该配置包含多个配置项,请点击链接查看具体内容。
AutoreleasePool 在 macOS 上,配置每个托管线程是否接收隐式 NSAutoreleasePool。

参考资料
.NET 运行时配置设置:https://docs.microsoft.com/zh-cn/dotnet/core/run-time-config/
*.runtimeconfig.json:https://github.com/dotnet/cli/blob/rel/1.0.0/Documentation/specs/runtime-configuration-file.md
CLR 概述:https://docs.microsoft.com/zh-cn/dotnet/standard/clr

global.json

通过 global.json 文件,可设置 .NET CLI 工具使用的 .NET SDK 版本,该设置不会影响项目运行使用的运行时版本。

通常情况下 CLI 工具都会使用系统安装的最新版本的 SDK,此时不需要 global.json 文件。 但是在某些高级场景中,开发者可能会需要控制 CLI 使用的 SDK 版本,下面介绍如何进行此操作。

.NET SDK 会在当前目录和它的父级目录中寻找 global.json 文件,你可以使用 dotnet new globaljson 命令来创建 global.json 文件。

# 创建一个 global.json 文件并制定使用的 sdk 版本为 6.0.100
dotnet new globaljson --sdk-version 6.0.100

下面的 json 代码就是上述令所创建的 global.json 文件的内容。

{
  "sdk": {
    "version": "6.0.100"
  }
}

除了 version 配置项外,global.json 还包含 allowPrerelease、rollForward 等配置选项,详细内容请查阅 global.json 概述

参考资料
global.json 概述:https://docs.microsoft.com/zh-cn/dotnet/core/tools/global-json

.NET 卸载工具

可以使用 .NET 卸载工具 (dotnet-core-uninstall) 从系统中删除 .NET SDK 和运行时。该工具支持 Windows 和 macOS,目前不支持 Linux(2021年12月25日)。

工具可以在该页面下载。

安装

Windows 可以直接下载 msi 安装包进行安装。

在 macOS 上下载 dotnet-core-uninstall.tar.gz 包后,可以用下面的脚本进行安装。

mkdir -p ~/dotnet-core-uninstall
tar -zxf dotnet-core-uninstall.tar.gz -C ~/dotnet-core-uninstall
cd ~/dotnet-core-uninstall
./dotnet-core-uninstall -h
使用
dotnet-core-uninstall list #该命令列出了已安装的 .NET SDK 和运行时,可以通过此工具将其删除
dotnet-core-uninstall remove --all --sdk #删除所有sdk
dotnet-core-uninstall remove 1.1.11 --runtime #删除版本为 1.1.11的运行时

更多内容请查看参考资料,另外 Windows 平台也可以通过【添加或删除程序】功能来卸载 .NET SDK 和运行时。

参考资料
.NET 卸载工具:https://docs.microsoft.com/zh-cn/dotnet/core/additional-tools/uninstall-tool

.NET SDK 遥测

.NET SDK 包含遥测功能,该功能用于收集 CLI 命令的使用情况数据以及异常信息,此页面列出了该功能收集的数据点。

要禁用该功能只需设置环境变量DOTNET_CLI_TELEMETRY_OPTOUT为 1 或 true。

另外,该功能收集的统计数据可以在此页面中查看。

参考资料
.NET SDK 遥测:https://docs.microsoft.com/zh-cn/dotnet/core/tools/telemetry