.NET 项目 SDK
.NET Core 和 .NET 5 及更高版本项目与软件开发工具包 (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.BlazorWebAssembly |
The .NET Blazor WebAssembly SDK | |
Microsoft.NET.Sdk.Razor |
.NET Razor SDK | |
Microsoft.NET.Sdk.Worker |
.NET 辅助角色服务 SDK | |
Microsoft.NET.Sdk.WindowsDesktop |
.NET 桌面 SDK,其中包括 Windows 窗体 (WinForms) 和 Windows Presentation Foundation (WPF)。* | https://github.com/dotnet/winforms 和 https://github.com/dotnet/wpf |
.NET SDK 是 .NET 的基本 SDK。 其他 SDK 引用 .NET SDK,与其他 SDK 关联的项目具有所有可用的 .NET SDK 属性。 例如,Web SDK 依赖于 .NET SDK 和 Razor SDK。
你还可以创建自己的 SDK,并通过 NuGet 进行分发。
* 从 .NET 5 开始,Windows 窗体和 Windows Presentation Foundation (WPF) 项目应指定 .NET SDK (Microsoft.NET.Sdk
),而不是 Microsoft.NET.Sdk.WindowsDesktop
。 对于这些项目,将 TargetFramework
设置为 net5.0-windows
并将 UseWPF
或 UseWindowsForms
设置为 true
的操作会自动导入 Windows 桌面 SDK。 如果你的项目面向 .NET 5 或更高版本,并指定 Microsoft.NET.Sdk.WindowsDesktop
SDK,则会收到生成警告 NETSDK1137。
项目文件
.NET 项目基于 MSBuild 格式。 具有扩展名(如用于 C# 项目的 .csproj 和用于 F# 项目的 .fsproj)的项目文件都是 XML 格式的 。 MSBuild 项目文件的根元素是 Project 元素。 Project
元素有一个可选的 Sdk
属性,该属性指定要使用的 SDK(和版本)。 若要使用 .NET 工具并构建你的代码,请将 Sdk
属性设置为可用 SDK 表中的其中一个 ID。
<Project Sdk="Microsoft.NET.Sdk">
...
</Project>
若要指定来自 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=netcoreapp2.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 属性表示。
.NET 桌面 SDK 对于 WPF 有更多包含和排除的内容。 有关详细信息,请参阅 WPF 默认包含和排除的内容。
生成错误
如果在项目文件中显式定义这些项中的任何项,可能会出现类似于以下内容的“NETSDK1022”生成错误:
包含重复的“Compile”项。 默认情况下,.NET SDK 包括项目目录中的“Compile”项。 可从项目文件中删除这些项,或如果想要在项目文件中显式包括它们,则将“EnableDefaultCompileItems”属性设为“false”。
包含重复的“EmbeddedResource”项。 默认情况下,.NET SDK 包括项目目录中的“EmbeddedResource”项。 可从项目文件中删除这些项,或如果想要在项目文件中显式包括它们,则将“EnableDefaultEmbeddedResourceItems”属性设为“false”。
若要解决此错误,请执行以下操作之一:
删除与上表中列出的隐式项匹配的显式
Compile
、EmbeddedResource
或None
项。若要禁用所有隐式文件包含,请将 EnableDefaultItems 属性设置为
false
:<PropertyGroup> <EnableDefaultItems>false</EnableDefaultItems> </PropertyGroup>
若要指定某些文件通过应用发布,仍可以使用相应的已知 MSBuild 机制来实现(例如
Content
元素)。可选择仅禁用
Compile
、EmbeddedResource
或None
glob,方法是将 EnableDefaultCompileItems、EnableDefaultEmbeddedResourceItems 或 EnableDefaultNoneItems 属性设置为false
:<PropertyGroup> <EnableDefaultCompileItems>false</EnableDefaultCompileItems> <EnableDefaultEmbeddedResourceItems>false</EnableDefaultEmbeddedResourceItems> <EnableDefaultNoneItems>false</EnableDefaultNoneItems> </PropertyGroup>
如果仅禁用
Compile
glob,则 Visual Studio 中的解决方案资源管理器仍将 *.cs 项显示为项目的一部分,并作为None
项包含在内。 若要禁用隐式None
glob,请将EnableDefaultNoneItems
也设置为false
。
隐式 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
global using
将针对基于项目的 SDK 的一组默认命名空间中的每个命名空间添加指令。 下表显示了这些默认命名空间。
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 | 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.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 Core 1.0 - 2.2 或 .NET Standard 1.0 - 2.0 为目标,则 .NET SDK 会添加对某些元包的隐式引用。 元包是一种基于框架的包,其中只包含对其他包的依赖项。 元包根据项目文件的 TargetFramework 或 TargetFrameworks 属性中指定的目标框架被隐式引用。
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<TargetFrameworks>netcoreapp2.1;net462</TargetFrameworks>
</PropertyGroup>
如果需要,可以使用 DisableImplicitFrameworkReferences 属性来禁用隐式包引用,并只添加对所需的框架或包的显式引用。
建议:
- 如果以 .NET Framework、.NET Core 1.0 - 2.2 或 .NET Standard 1.0 - 2.0 为目标,不要通过项目文件中的
<PackageReference>
项添加对Microsoft.NETCore.App
或NETStandard.Library
元包的显式引用。 对于 .NET Core 1.0 - 2.2 和 .NET Standard 1.0 - 2.0 项目,这些元包被隐式引用。 对于 .NET Framework 项目,如果在使用基于 .NET Standard 的 NuGet 包时需要任何版本的NETStandard.Library
,则 NuGet 会自动安装相应版本。 - 如果在以 .NET Core 1.0 - 2.2 为目标时需要特定版本的运行时,请在项目中使用
<RuntimeFrameworkVersion>
属性(例如,1.0.4
),而不是引用元包。 例如,如果在使用独立式部署,则可能需要特定补丁版本的 1.0.0 LTS 运行时。 - 如果在以 .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 峰会示例存储库。