单文件部署

通过将所有依赖应用程序的文件捆绑到一个二进制文件中,为应用程序开发人员提供一个具有吸引力的选项,那就是将应用程序作为单个文件进行部署和分发。 单文件部署可用于依赖框架的部署模型独立应用程序

独立应用程序中单个文件的大小很大,因为它包含运行时和框架库。 在 .NET 6 中,可以通过发布剪裁来减小与剪裁兼容的应用程序的总大小。 单文件部署选项可与 ReadyToRunTrim 发布选项结合使用。

重要

要在 Windows 7 上运行单个文件应用,必须使用 .NET 运行时 6.0.3 或更高版本。

示例项目文件

下面是指定单文件发布的示例项目文件:

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

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <PublishSingleFile>true</PublishSingleFile>
    <SelfContained>true</SelfContained>
    <RuntimeIdentifier>win-x64</RuntimeIdentifier>
  </PropertyGroup>

</Project>

这些属性具有下列函数:

  • PublishSingleFile。 启用单文件发布。 此外,还会在 dotnet build 期间启用单文件警告。
  • SelfContained。 确定应用是独立的还是依赖于框架的。
  • RuntimeIdentifier。 指定目标 OS 和 CPU 类型。 默认情况下,还会设置 <SelfContained>true</SelfContained>

单文件应用始终特定于 OS 和体系结构。 需要为每个配置发布,例如 Linux x64、Linux Arm64、Windows x64 等。

单个文件中包含运行时配置文件,例如 *.runtimeconfig.json 和 *.deps.json。 如果需要额外的配置文件,可将其放在单个文件旁边。

发布单文件应用

使用 dotnet publish 命令发布单文件应用程序。

  1. <PublishSingleFile>true</PublishSingleFile> 添加到项目文件。

    此更改将针对独立发布生成一个单文件应用。 它还会在生成期间显示单文件兼容性警告。

    <PropertyGroup>
        <PublishSingleFile>true</PublishSingleFile>
    </PropertyGroup>
    
  2. 使用 dotnet publish -r <RID> 针对特定运行时标识符发布应用

    以下示例将 Windows 应用作为独立的单一文件应用程序发布。

    dotnet publish -r win-x64

    以下示例将 Linux 应用作为依赖框架的单一文件应用程序发布。

    dotnet publish -r linux-x64 --self-contained false

应在项目文件中设置 <PublishSingleFile> 以在生成期间启用文件分析,但也可以将这些选项作为 dotnet publish 参数传递:

dotnet publish -r linux-x64 -p:PublishSingleFile=true --self-contained false

有关详细信息,请参阅使用 .NET CLI 发布 .NET Core 应用

将文件排除在嵌入之外

通过设置以下元数据,可明确指定不在单文件中嵌入某些文件:

<ExcludeFromSingleFile>true</ExcludeFromSingleFile>

例如,若要将某些文件放置在发布目录中,但不将它们捆绑到文件中:

<ItemGroup>
  <Content Update="Plugin.dll">
    <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
    <ExcludeFromSingleFile>true</ExcludeFromSingleFile>
  </Content>
</ItemGroup>

在绑定内包括 PDB 文件

可以使用下面的设置将程序集的 PDB 文件嵌入到程序集本身 (.dll)。 由于符号是程序集的一部分,因此,它们也是应用程序的一部分:

<DebugType>embedded</DebugType>

例如,将以下属性添加到程序集的项目文件,以将 PDB 文件嵌入到该程序集:

<PropertyGroup>
  <DebugType>embedded</DebugType>
</PropertyGroup>

其他注意事项

单文件应用程序旁边将具有所有相关的 PDB 文件,并且默认情况下不会绑定。 如果要在生成的项目的程序集内包含 PDB,请将 DebugType 设置为 embedded。 请查阅在绑定内包括 PDB 文件

托管的 C++ 应组件不太适合进行单文件部署。 建议使用 C# 或其他非托管 C++ 语言编写应用程序来实现单文件兼容。

本机库

仅将托管的 DLL 与应用捆绑到一个可执行文件中。 当应用启动时,托管的 DLL 将被提取并加载到内存中,从而避免提取到文件夹中。 使用此方法,托管二进制文件将嵌入单文件捆绑包中,但核心运行时本身的本机二进制文件是单独的文件。

若要嵌入这些文件以进行提取并获取一个输出文件,请将属性 IncludeNativeLibrariesForSelfExtract 设置为 true

指定 IncludeAllContentForSelfExtract 将在运行可执行文件之前提取所有文件,包括托管程序集。 这可能有助于解决罕见的应用程序兼容性问题。

重要

如果进行提取,则会在应用启动前将文件提取到磁盘:

  • 如果 DOTNET_BUNDLE_EXTRACT_BASE_DIR 环境变量设为路径,则会将文件提取到该路径下的目录。
  • 此外,如果在 Linux 或 MacOS 上运行,则会将文件提取到 $HOME/.net 下的目录。
  • 如果在 Windows 上运行,则会将文件提取到 %TEMP%/.net 下的目录。

若要防止篡改,这些目录不应由具有不同权限的用户或服务写入。 在大多数 Linux 和 MacOS 系统上,不要使用 /tmp 或 /var/tmp。

注意

在某些 Linux 环境下(例如在 systemd 下),由于未定义 $HOME,无法进行默认提取。 在这种情况下,建议显式设置 $DOTNET_BUNDLE_EXTRACT_BASE_DIR

对于 systemd,还可将服务单元文件中的 DOTNET_BUNDLE_EXTRACT_BASE_DIR 定义为 %h/.netsystemd 可为运行该服务的帐户将其正确地扩展为 $HOME/.net

[Service]
Environment="DOTNET_BUNDLE_EXTRACT_BASE_DIR=%h/.net"

API 不兼容

某些 API 与单文件部署不兼容。 如果应用程序使用这些 API,可能需要进行修改。 如果使用第三方框架或包,则它们可能使用了这样的 API 并需要修改。 出现问题的最常见原因是依赖于应用程序附带的文件或 DLL 的文件路径。

下表提供了用于单文件的相关运行时库 API 详细信息。

API 注意
Assembly.CodeBase 引发 PlatformNotSupportedException
Assembly.EscapedCodeBase 引发 PlatformNotSupportedException
Assembly.GetFile 引发 IOException
Assembly.GetFiles 引发 IOException
Assembly.Location 返回空字符串。
AssemblyName.CodeBase 返回 null
AssemblyName.EscapedCodeBase 返回 null
Module.FullyQualifiedName 返回值为 <Unknown> 的字符串,或引发异常。
Marshal.GetHINSTANCE 返回 -1。
Module.Name 返回值为 <Unknown> 的字符串。

我们提供了关于修复常见问题的一些建议:

捆绑前对二进制文件进行后处理

某些工作流需要在捆绑之前对二进制文件进行后处理。 一个常见示例是签名。 dotnet SDK 提供 MSBuild 扩展点,以允许在单文件捆绑之前处理二进制文件。 可用 API 包括:

  • 将在 GenerateSingleFileBundle 之前调用的目标 PrepareForBundle
  • 包含将捆绑的所有文件的 <ItemGroup><FilesToBundle /></ItemGroup>
  • 将指定 apphost 模板的属性 AppHostFile。 后处理可能需要将 apphost 从处理中排除。

若要插入此内容,需要创建将在 PrepareForBundleGenerateSingleFileBundle 之间执行的目标。

请考虑以下 .NET 项目 Target 节点示例:

<Target Name="MySignedBundledFile" BeforeTargets="GenerateSingleFileBundle" DependsOnTargets="PrepareForBundle">

工具可能需要在签名过程中复制文件。 如果原始文件是不属于生成的共享项(例如,该文件来自 NuGet 缓存),则可能会发生这种情况。 在这种情况下,工具预期会修改相应 FilesToBundle 项的路径,以指向修改后的副本。

在单文件应用中压缩程序集

在嵌入式程序集上启用压缩后,可以创建单文件应用。 将 EnableCompressionInSingleFile 属性设置为 true。 生成的单个文件将包含所有已压缩的嵌入式程序集,这可以显著减小可执行文件的大小。

压缩会导致性能下降。 在应用程序启动时,必须将程序集解压缩到内存中,这需要花费一些时间。 在使用压缩之前,建议衡量启用压缩造成的大小更改和启动开销影响。 这种影响在不同的应用程序之间有很大的差异。

检查单文件应用

可以使用 ILSpy 工具检查单文件应用。 该工具可以显示捆绑到应用程序中的所有文件,并且可以检查托管程序集的内容。

请参阅