新式 .NET 项目与项目软件开发工具包 (SDK) 关联。 每个项目 SDK 都是一组 MSBuild 目标和相关的任务,它们负责编译、打包和发布代码。 引用项目 SDK 的项目有时称为“SDK 样式的项目”。
可用的 SDK
可用的 SDK 包括:
| ID | 描述 | 存储库 |
|---|---|---|
Microsoft.NET.Sdk |
.NET SDK | https://github.com/dotnet/sdk |
Microsoft.NET.Sdk.Web |
.NET Web SDK | https://github.com/dotnet/sdk |
Microsoft.NET.Sdk.Razor |
.NET Razor SDK | https://github.com/dotnet/aspnetcore |
Microsoft.NET.Sdk.BlazorWebAssembly |
The .NET Blazor WebAssembly SDK | https://github.com/dotnet/aspnetcore |
Microsoft.NET.Sdk.Worker |
.NET 工作服务 SDK | https://github.com/dotnet/aspnetcore |
Aspire.AppHost.Sdk |
.NET Aspire SDK | https://github.com/dotnet/aspire |
MSTest.Sdk |
MSTest SDK | https://github.com/microsoft/testfx |
.NET SDK 是 .NET 的基本 SDK。 其他 SDK 引用 .NET SDK,与其他 SDK 关联的项目具有所有可用的 .NET SDK 属性。 例如,Web SDK 依赖于 .NET SDK 和 Razor SDK。
对于 Windows 窗体和 Windows Presentation Foundation (WPF) 项目,请指定 .NET SDK (Microsoft.NET.Sdk),并在项目文件中设置一些其他属性。 有关详细信息,请参阅启用 .NET Desktop SDK。
MSBuild SDK(您可以用来配置和扩展构建)列在MSBuild SDKs中。
你还可以创建自己的 SDK,并通过 NuGet 进行分发。
项目文件
.NET 项目基于 MSBuild 格式。 具有扩展名(如用于 C# 项目的 .csproj 和用于 F# 项目的 .fsproj)的项目文件都是 XML 格式的 。 MSBuild 项目文件的根元素是 Project 元素。
Project 元素有一个可选的 Sdk 属性,该属性指定要使用的 SDK(和版本)。 若要使用 .NET 工具并构建你的代码,请将 Sdk 属性设置为可用 SDK 表中的其中一个 ID。
<Project Sdk="Microsoft.NET.Sdk">
<!-- Omitted for brevity... -->
</Project>
属性 Project/Sdk 和 Sdk 元素启用附加 SDK。 请考虑以下示例,其中 Aspire SDK (Aspire.AppHost.Sdk) 将添加到项目顶部 Microsoft.NET.Sdk:
<Project Sdk="Microsoft.NET.Sdk">
<Sdk Name="Aspire.AppHost.Sdk" Version="9.0.0" />
<!-- Omitted for brevity... -->
</Project>
在前面的项目文件中,这两个 SDK 用于解析具有累加性的依赖项。 有关详细信息,请参阅 Aspire SDK。
若要指定来自 NuGet 的 SDK,请在名称末尾包含版本,或者在 global.json 文件中指定名称和版本。
<Project Sdk="MSBuild.Sdk.Extras/2.0.54">
...
</Project>
另一种指定 SDK 的方法是使用顶层 Sdk 元素:
<Project>
<Sdk Name="Microsoft.NET.Sdk" />
...
</Project>
以这些方式之一引用 SDK 可以极大地简化 .NET 的项目文件。 在评估项目时,MSBuild 在项目文件的顶部和底部分别为 Sdk.props 和 Sdk.targets 添加隐式导入。
<Project>
<!-- Implicit top import -->
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
...
<!-- Implicit bottom import -->
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
</Project>
提示
在 Windows 计算机上,Sdk.props 和 Sdk.targets 文件位于 %ProgramFiles%\dotnet\sdk\[version]\Sdks\Microsoft.NET.Sdk\Sdk 文件夹中。
预处理项目文件
使用 dotnet msbuild -preprocess 命令,可以看到 MSBuild 在包含 SDK 及其目标之后所显示的完全扩展的项目。
预处理开关在dotnet msbuild命令中显示哪些文件被导入、它们的来源以及它们对构建的贡献,而无需实际构建项目。
如果项目有多个目标框架,请将命令的结果指定为 MSBuild 属性,使其仅侧重于框架之一。 例如:
dotnet msbuild -property:TargetFramework=net8.0 -preprocess:output.xml
默认包含和排除的内容
SDK 中定义了 Compile 项、嵌入的资源和 None 项默认包含和排除的内容。 与非 SDK .NET 框架项目不同,你无需在项目文件中指定这些项,因为默认设置涵盖了最常见的用例。 此行为使得项目文件更小、更易于理解和手动编辑(如需要)。
下表显示在 .NET SDK 中包含和排除的元素和 glob:
| 元素 | 包含 glob | 排除 glob | 删除 glob |
|---|---|---|---|
| Compile | **/*.cs(或其他语言扩展名) | **/*。用户;**/*.*proj;**/*.sln(x);**/*.vssscc | 不适用 |
| EmbeddedResource | **/*.resx | **/*。用户;**/*.*proj;**/*.sln(x);**/*.vssscc | 不适用 |
| None | **/* | **/*。用户;**/*.*proj;**/*.sln(x);**/*.vssscc | **/*.cs;**/*.resx |
注意
默认情况下,由 ./bin 和 ./obj MSBuild 属性表示的 $(BaseOutputPath) 和 $(BaseIntermediateOutputPath) 文件夹不包含在 glob 中。 排除由 DefaultItemExcludes 属性表示。
对于 WPF,.NET Desktop SDK 有更多包含和排除项。 有关详细信息,请参阅 WPF 默认包含和排除的内容。
如果在项目文件中显式定义这些项中的任何项,可能会出现 NETSDK1022 生成错误。 有关如何解决此错误的信息,请参阅 NETSDK1022:包含重复项。
隐式 using 指令
从 .NET 6 开始,隐式 global using 指令将添加到新的 C# 项目中。 这意味着可以使用这些命名空间中定义的类型,而无需指定完全限定的名称或手动添加 using 指令。 隐式方面是指向项目的 obj 目录中生成的文件添加 指令这一事实。global using
为使用以下 SDK 之一的项目添加隐式 global using 指令:
Microsoft.NET.SdkMicrosoft.NET.Sdk.WebMicrosoft.NET.Sdk.WorkerMicrosoft.NET.Sdk.WindowsDesktop
为基于项目 SDK 的一组默认命名空间中的每个命名空间添加 global using 指令。 下表显示了这些默认命名空间。
| SDK | 默认命名空间 |
|---|---|
| Microsoft.NET.Sdk | System System.Collections.Generic System.IO System.Linq System.Net.Http System.Threading System.Threading.Tasks |
| Microsoft.NET.Sdk.Web | Microsoft.NET.Sdk 命名空间 System.Net.Http.Json Microsoft.AspNetCore.Builder Microsoft.AspNetCore.Hosting Microsoft.AspNetCore.Http Microsoft.AspNetCore.Routing Microsoft.Extensions.Configuration Microsoft.Extensions.DependencyInjection Microsoft.Extensions.Hosting Microsoft.Extensions.Logging |
| Microsoft.NET.Sdk.Worker | Microsoft.NET.Sdk 命名空间 Microsoft.Extensions.Configuration Microsoft.Extensions.DependencyInjection Microsoft.Extensions.Hosting Microsoft.Extensions.Logging |
| Microsoft.NET.Sdk.WindowsDesktop(Windows Forms) | Microsoft.NET.Sdk 命名空间 System.Drawing System.Windows.Forms |
| Microsoft.NET.Sdk.WindowsDesktop (WPF) | Microsoft.NET.Sdk 命名空间 已删除 System.IO 已删除 System.Net.Http |
若要禁用此功能,或要在现有的 C# 项目中启用隐式 global using 指令,可通过 ImplicitUsingsMSBuild 属性实现。
可以通过向项目文件添加 global using 项(或针对 Visual Basic 项目添加 Using 项)来指定其他隐式 Import 指令,例如:
<ItemGroup>
<Using Include="System.IO.Pipes" />
</ItemGroup>
注意
从 .NET 8 SDK 开始,当面向 .NET Framework 时,System.Net.Http将不再包含在 Microsoft.NET.Sdk 中。
隐式包引用
如果你的项目以 .NET Standard 1.0-2.0 为目标,则 .NET SDK 会添加对某些元包的隐式引用。 元包是一种基于框架的包,其中只包含对其他包的依赖项。 元包根据项目文件的 TargetFramework 或 TargetFrameworks(复数)属性中指定的目标框架被隐式引用。
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net462</TargetFrameworks>
</PropertyGroup>
如果需要,可以使用 DisableImplicitFrameworkReferences 属性来禁用隐式包引用,并只添加对所需的框架或包的显式引用。
建议:
- 如果以 .NET Framework 或 .NET Standard 1.0-2.0 为目标,不要通过项目文件中的
NETStandard.Library项添加对<PackageReference>元包的显式引用。 对于 .NET Standard 1.0-2.0 项目,这些元包被隐式引用。 对于 .NET Framework 项目,如果在使用基于 .NET Standard 的 NuGet 包时需要任何版本的NETStandard.Library,则 NuGet 会自动安装相应版本。 - 如果在以 .NET Standard 1.0-2.0 为目标时需要特定版本的
NETStandard.Library元包,则可以使用<NetStandardImplicitPackageVersion>属性,并设置所需的版本。
生成事件
在 SDK 样式的项目中,请使用名为 PreBuild 或 PostBuild 的 MSBuild 目标,并设置 BeforeTargets 的 PreBuild 属性或 AfterTargets 的 PostBuild 属性。
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
<Exec Command=""$(ProjectDir)PreBuildEvent.bat" "$(ProjectDir)..\" "$(ProjectDir)" "$(TargetDir)"" />
</Target>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="echo Output written to $(TargetDir)" />
</Target>
注意
- 可以为 MSBuild 目标使用任何名称。 但是,Visual Studio IDE 会识别
PreBuild和PostBuild目标,因此通过使用这些名称,可以在 IDE 中编辑命令。 - 不建议在 SDK 样式的项目中使用属性
PreBuildEvent和PostBuildEvent,因为无法解析$(ProjectDir)这样的宏。 例如,以下代码是不受支持的:
<PropertyGroup>
<PreBuildEvent>"$(ProjectDir)PreBuildEvent.bat" "$(ProjectDir)..\" "$(ProjectDir)" "$(TargetDir)"</PreBuildEvent>
</PropertyGroup>
自定义生成
可以通过多种方式自定义构建。 建议通过将属性作为参数传递给 msbuild 或 dotnet 命令来重写该属性。 还可以将属性添加到项目文件或 Directory.Build.props 文件中。 有关 .NET 项目的有用属性列表,请参阅 .NET SDK 项目的 MSBuild 参考。
提示
从命令行创建新的 Directory.Build.props 文件的一种简单方法是使用存储库根目录中的命令 dotnet new buildprops。
自定义目标
.NET 项目可以打包自定义的 MSBuild 目标和属性,以供使用该包的项目使用。 如果您希望实现以下目的,请使用这种类型的可扩展性:
- 扩展生成过程。
- 访问生成过程的工件,如生成的文件。
- 检查调用生成的配置。
通过在项目的<package_id>.targets文件夹中以 <package_id>.props 或 Contoso.Utility.UsefulStuff.targets(例如 )的形式放置文件,可以添加自定义生成目标或属性。
以下 XML 是 .csproj 文件中的一个片段,该文件指示 dotnet pack 命令打包的内容。
<ItemGroup Label="dotnet pack instructions"> 元素将目标文件放入包内的生成文件夹中。
<Target Name="CollectRuntimeOutputs" BeforeTargets="_GetPackageFiles"> 元素将程序集和 .json 文件放入生成文件夹 。
<Project Sdk="Microsoft.NET.Sdk">
...
<ItemGroup Label="dotnet pack instructions">
<Content Include="build\*.targets">
<Pack>true</Pack>
<PackagePath>build\</PackagePath>
</Content>
</ItemGroup>
<Target Name="CollectRuntimeOutputs" BeforeTargets="_GetPackageFiles">
<!-- Collect these items inside a target that runs after build but before packaging. -->
<ItemGroup>
<Content Include="$(OutputPath)\*.dll;$(OutputPath)\*.json">
<Pack>true</Pack>
<PackagePath>build\</PackagePath>
</Content>
</ItemGroup>
</Target>
...
</Project>
若要在项目中使用自定义目标,请添加指向包及其版本的 PackageReference 元素。 与工具不同,自定义目标包包含在消费项目的依赖项闭包中。
你可以配置自定义目标的使用方式。 由于它是 MSBuild 目标,因此会依赖于给定的目标并在另一个目标后运行,也可使用 dotnet msbuild -t:<target-name> 命令手动调用。 若要提供更好的用户体验,可以合并基于项目的工具和自定义目标。 在此方案中,每个项目工具接受所需的任何参数,并将其转换为执行目标所需的 dotnet msbuild 调用。 您可以在 MVP Summit 2016 黑客马拉松示例 存储库的 dotnet-packer 项目中看到这种协同作用的示例。