.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 https://github.com/dotnet/aspnetcore
Microsoft.NET.Sdk.Razor .NET Razor SDK https://github.com/dotnet/aspnetcore
Microsoft.NET.Sdk.Worker .NET 辅助角色服务 SDK
Microsoft.NET.Sdk.WindowsDesktop .NET 桌面 SDK,其中包括 Windows 窗体 (WinForms) 和 Windows Presentation Foundation (WPF)。* https://github.com/dotnet/winformshttps://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 并将 UseWPFUseWindowsForms 设置为 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.propsSdk.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”。

若要解决此错误,请执行以下操作之一:

  • 删除与上表中列出的隐式项匹配的显式 CompileEmbeddedResourceNone 项。

  • 若要禁用所有隐式文件包含,请将 EnableDefaultItems 属性设置为 false

    <PropertyGroup>
      <EnableDefaultItems>false</EnableDefaultItems>
    </PropertyGroup>
    

    若要指定某些文件通过应用发布,仍可以使用相应的已知 MSBuild 机制来实现(例如 Content 元素)。

  • 可选择仅禁用 CompileEmbeddedResourceNone glob,方法是将 EnableDefaultCompileItemsEnableDefaultEmbeddedResourceItemsEnableDefaultNoneItems 属性设置为 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

为基于项目 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 指令,可通过 ImplicitUsingsMSBuild 属性实现。

可以通过向项目文件添加 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 会添加对某些元包的隐式引用。 元包是一种基于框架的包,其中只包含对其他包的依赖项。 元包根据项目文件的 TargetFrameworkTargetFrameworks 属性中指定的目标框架被隐式引用。

<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.AppNETStandard.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 样式的项目中,请使用名为 PreBuildPostBuild 的 MSBuild 目标,并设置 PreBuildBeforeTargets 属性或 PostBuildAfterTargets 属性。

<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
    <Exec Command="&quot;$(ProjectDir)PreBuildEvent.bat&quot; &quot;$(ProjectDir)..\&quot; &quot;$(ProjectDir)&quot; &quot;$(TargetDir)&quot;" />
</Target>

<Target Name="PostBuild" AfterTargets="PostBuildEvent">
   <Exec Command="echo Output written to $(TargetDir)" />
</Target>

注意

  • 可以为 MSBuild 目标使用任何名称。 但是,Visual Studio IDE 会识别 PreBuildPostBuild 目标,因此通过使用这些名称,可以在 IDE 中编辑命令。
  • 不建议在 SDK 样式的项目中使用属性 PreBuildEventPostBuildEvent,因为无法解析 $(ProjectDir) 这样的宏。 例如,以下代码是不受支持的:
<PropertyGroup>
  <PreBuildEvent>"$(ProjectDir)PreBuildEvent.bat" "$(ProjectDir)..\" "$(ProjectDir)" "$(TargetDir)"</PreBuildEvent>
</PropertyGroup>

自定义生成

可以通过多种方式自定义生成。 建议通过将属性作为参数传递给 msbuilddotnet 命令来重写该属性。 还可以将属性添加到项目文件或 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 峰会示例存储库。

请参阅