.NET 项目 SDK
新式 .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 | |
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。
你还可以创建自己的 SDK,并通过 NuGet 进行分发。
对于 Windows 窗体和 Windows Presentation Foundation (WPF) 项目,请指定 .NET SDK (Microsoft.NET.Sdk
),并在项目文件中设置一些其他属性。 有关详细信息,请参阅启用 .NET Desktop SDK。
项目文件
.NET 项目基于 MSBuild 格式。 具有扩展名(如用于 C# 项目的 .csproj 和用于 F# 项目的 .fsproj)的项目文件都是 XML 格式的 。 MSBuild 项目文件的根元素是 Project 元素。 Project
元素有一个可选的 Sdk
属性,该属性指定要使用的 SDK(和版本)。 若要使用 .NET 工具并构建你的代码,请将 Sdk
属性设置为可用 SDK 表中的其中一个 ID。
<Project Sdk="Microsoft.NET.Sdk">
...
</Project>
从 .NET Aspire 9 开始,前面的示例可以改为使用 .NET Aspire SDK。
<Project Sdk="Microsoft.NET.Sdk">
<Sdk Name="Aspire.AppHost.Sdk" Version="9.0.0-rc.1.24511.1" />
<!-- Omitted for brevity... -->
</Project>
有关详细信息,请参阅 .NET Aspire 工具和设置。
若要指定来自 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(或其他语言扩展名) | **/*.user; **/*.*proj; **/*.sln; **/*.vssscc | 空值 |
EmbeddedResource | **/*.resx | **/*.user; **/*.*proj; **/*.sln; **/*.vssscc | 空值 |
None | **/* | **/*.user; **/*.*proj; **/*.sln; **/*.vssscc | **/*.cs; **/*.resx |
注意
默认情况下,由 $(BaseOutputPath)
和 $(BaseIntermediateOutputPath)
MSBuild 属性表示的 ./bin
和 ./obj
文件夹不包含在 glob 中。 排除由 DefaultItemExcludes 属性表示。
对于 WPF,.NET Desktop SDK 有更多包含和排除项。 有关详细信息,请参阅 WPF 默认包含和排除的内容。
如果在项目文件中显式定义这些项中的任何项,可能会出现 NETSDK1022 生成错误。 有关如何解决此错误的信息,请参阅 NETSDK1022:包含重复项。
隐式 using 指令
从 .NET 6 开始,隐式 global using
指令将添加到新的 C# 项目中。 这意味着可以使用这些命名空间中定义的类型,而无需指定完全限定的名称或手动添加 using
指令。 隐式方面是指向项目的 obj 目录中生成的文件添加 global using
指令这一事实。
为使用以下 SDK 之一的项目添加隐式 global using
指令:
Microsoft.NET.Sdk
Microsoft.NET.Sdk.Web
Microsoft.NET.Sdk.Worker
Microsoft.NET.Sdk.WindowsDesktop
为基于项目 SDK 的一组默认命名空间中的每个命名空间添加 global using
指令。 下表显示了这些默认命名空间。
SDK 中 IsInRole 中的声明 | 默认命名空间 |
---|---|
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 窗体) | Microsoft.NET.Sdk 命名空间 System.Drawing System.Windows.Forms |
Microsoft.NET.Sdk.WindowsDesktop (WPF) | Microsoft.NET.Sdk 命名空间 已删除 System.IO 已删除 System.Net.Http |
若要禁用此功能,或要在现有的 C# 项目中启用隐式 global using
指令,可通过 ImplicitUsings
MSBuild 属性实现。
可以通过向项目文件添加 Using
项(或针对 Visual Basic 项目添加 Import
项)来指定其他隐式 global using
指令,例如:
<ItemGroup>
<Using Include="System.IO.Pipes" />
</ItemGroup>
隐式包引用
如果你的项目以 .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 为目标,不要通过项目文件中的
<PackageReference>
项添加对NETStandard.Library
元包的显式引用。 对于 .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 目标,并设置 PreBuild
的 BeforeTargets
属性或 PostBuild
的 AfterTargets
属性。
<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
调用。 有关此类协同作用的示例,请访问 dotnet-packer
项目中的 2016 年编程马拉松 MVP 峰会示例存储库。