.NETNativeAOT编译技能 dotnet-native-aot

这个技能涵盖了.NET Native AOT(提前编译)的完整流程,用于将.NET应用程序编译成本地二进制文件,优化启动性能、减少内存占用和部署大小。包括配置PublishAot、使用ILLink描述符进行类型保留、反射自由编码模式、P/Invoke考虑、二进制大小优化、自包含部署以及ASP.NET Core Native AOT支持。适用于AOT-first设计、编译优化和DevOps场景。关键词:.NET Native AOT, 编译优化, 部署策略, ASP.NET Core, AOT-first设计, DevOps, 软件开发。

DevOps 0 次安装 0 次浏览 更新于 3/6/2026

名称: dotnet-native-aot 描述: “发布Native AOT二进制文件。PublishAot配置、ILLink描述符、P/Invoke、大小优化。” 用户可调用: false

dotnet-native-aot

完整的Native AOT编译流程用于.NET 8+应用程序:PublishAot配置、用于类型保留的ILLink描述符XML、反射自由编码模式、P/Invoke考虑、二进制大小优化、使用runtime-deps基础镜像的自包含部署以及诊断分析器(EnableAotAnalyzer/EnableTrimAnalyzer)。

版本假设: .NET 8.0+基线。Native AOT用于ASP.NET Core Minimal APIs和console apps在.NET 8中发布。 .NET 9改进了修剪警告和库兼容性。 .NET 10增强了请求委托生成器并扩展了Minimal API AOT支持。

范围

  • PublishAot MSBuild配置(应用vs库)
  • 诊断分析器(EnableAotAnalyzer、EnableTrimAnalyzer)
  • 用于类型保留的ILLink描述符XML
  • 反射自由编码模式
  • 使用LibraryImport源生成的P/Invoke
  • 二进制大小优化和自包含部署
  • ASP.NET Core Native AOT(Minimal APIs、CreateSlimBuilder)

超出范围

  • MAUI iOS/Mac Catalyst AOT流程 – 参见[技能:dotnet-maui-aot]
  • AOT优先设计模式(源生成、DI、序列化) – 参见[技能:dotnet-aot-architecture]
  • 修剪安全库编写 – 参见[技能:dotnet-trimming]
  • WASM AOT用于Blazor/Uno – 参见[技能:dotnet-aot-wasm]
  • 源生成器编写(Roslyn API) – 参见[技能:dotnet-csharp-source-generators]
  • DI容器模式 – 参见[技能:dotnet-csharp-dependency-injection]
  • 序列化深度 – 参见[技能:dotnet-serialization]
  • 容器部署编排 – 参见[技能:dotnet-containers]

交叉引用:[技能:dotnet-aot-architecture]用于AOT优先设计模式,[技能:dotnet-trimming]用于修剪安全库编写,[技能:dotnet-aot-wasm]用于WebAssembly AOT,[技能:dotnet-maui-aot]用于MAUI特定AOT,[技能:dotnet-containers]用于runtime-deps基础镜像,[技能:dotnet-serialization]用于AOT安全序列化,[技能:dotnet-csharp-source-generators]用于源生成作为AOT启用器,[技能:dotnet-csharp-dependency-injection]用于AOT安全DI,[技能:dotnet-native-interop]用于一般P/Invoke模式和跨平台库解析。


PublishAot配置

启用Native AOT

<!-- 应用 .csproj -->
<PropertyGroup>
  <PublishAot>true</PublishAot>
</PropertyGroup>
# 发布为Native AOT
dotnet publish -c Release -r linux-x64

# 为特定目标发布
dotnet publish -c Release -r win-x64
dotnet publish -c Release -r osx-arm64

MSBuild属性:应用vs库

应用和库使用不同的MSBuild属性。不要混合使用。

对于应用程序(console apps、ASP.NET Core Minimal APIs):

<PropertyGroup>
  <!-- 在发布时启用Native AOT编译 -->
  <PublishAot>true</PublishAot>

  <!-- 在开发期间启用分析器(不仅仅是发布) -->
  <EnableAotAnalyzer>true</EnableAotAnalyzer>
  <EnableTrimAnalyzer>true</EnableTrimAnalyzer>
</PropertyGroup>

对于库(NuGet包、共享类库):

<PropertyGroup>
  <!-- 声明库是AOT兼容的(自动启用分析器) -->
  <IsAotCompatible>true</IsAotCompatible>
  <!-- 声明库是修剪安全的(自动启用修剪分析器) -->
  <IsTrimmable>true</IsTrimmable>
</PropertyGroup>

IsAotCompatibleIsTrimmable自动启用AOT和修剪分析器。不要在库项目中设置PublishAot – 库不发布为独立可执行文件。


诊断分析器

在开发期间启用AOT和修剪分析器以在发布前捕获问题:

<PropertyGroup>
  <EnableAotAnalyzer>true</EnableAotAnalyzer>
  <EnableTrimAnalyzer>true</EnableTrimAnalyzer>
</PropertyGroup>

无需发布的分析

运行分析而无需完整发布:

# 分析AOT兼容性而无需发布
dotnet build /p:EnableAotAnalyzer=true /p:EnableTrimAnalyzer=true

# 查看每次出现的警告(未按程序集分组)
dotnet build /p:EnableAotAnalyzer=true /p:EnableTrimAnalyzer=true /p:TrimmerSingleWarn=false

这报告IL2xxx(修剪)和IL3xxx(AOT)警告而不产生本地二进制文件,实现开发期间的快速反馈。

常见诊断代码

代码 类别 含义
IL2026 修剪 成员具有[RequiresUnreferencedCode] – 修剪后可能中断
IL2046 修剪 修剪属性在基类/派生类型间不匹配
IL2057-IL2072 修剪 修剪器无法分析的各种反射使用
IL3050 AOT 成员具有[RequiresDynamicCode] – 在运行时生成代码
IL3051 AOT [RequiresDynamicCode]属性不匹配

用于类型保留的ILLink描述符

当代码使用修剪器无法静态分析的反射时,使用ILLink描述符XML来保留类型。不要使用遗留的RD.xml它是.NET Native/UWP格式,被现代.NET AOT静默忽略。

ILLink描述符XML

<!-- ILLink.Descriptors.xml -->
<linker>
  <!-- 保留类型的所有公共成员 -->
  <assembly fullname="MyApp">
    <type fullname="MyApp.Models.LegacyConfig" preserve="all" />
    <type fullname="MyApp.Services.PluginLoader">
      <method name="LoadPlugin" />
    </type>
  </assembly>

  <!-- 保留整个外部程序集 -->
  <assembly fullname="IncompatibleLibrary" preserve="all" />
</linker>
<!-- 在.csproj中注册 -->
<ItemGroup>
  <TrimmerRootDescriptor Include="ILLink.Descriptors.xml" />
</ItemGroup>

[DynamicDependency]属性

用于代码中的定向保留(对于小的、本地化的情况优于ILLink XML):

using System.Diagnostics.CodeAnalysis;

// 保留特定方法
[DynamicDependency(nameof(LegacyConfig.Initialize), typeof(LegacyConfig))]
public void ConfigureApp() { /* ... */ }

// 保留所有公共成员
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(PluginBase))]
public void LoadPlugins() { /* ... */ }

何时使用哪种

场景 方法
一个或两个方法/类型 [DynamicDependency]属性
整个程序集或多个类型 ILLink描述符XML
第三方库非AOT安全 ILLink描述符XML或<TrimmerRootAssembly>
您自己的代码具有分析的反射 重构为源生成器(最佳长期方案)

反射自由模式

Native AOT最好与完全避免运行时反射的代码配合使用。用编译时替代方案替换反射模式。

反射模式 AOT安全替代方案
Activator.CreateInstance<T>() 工厂方法或显式new T()
Type.GetProperties() 用于映射 Mapperly源生成器或手动映射
Assembly.GetTypes() 用于DI扫描 显式services.AddScoped<T>()
JsonSerializer.Deserialize<T>(json) JsonSerializer.Deserialize(json, Context.Default.T)
MethodInfo.Invoke() 用于调度 switch基于类型或接口调度

参见[技能:dotnet-aot-architecture]以获取全面的AOT优先设计模式。


P/Invoke考虑

P/Invoke(平台调用)到本地库的调用通常与Native AOT兼容,但需要注意:

直接P/Invoke(首选)

// 直接P/Invoke -- AOT兼容,无运行时编组开销
[LibraryImport("libsqlite3", EntryPoint = "sqlite3_open")]
internal static partial int Sqlite3Open(
    [MarshalAs(UnmanagedType.LPStr)] string filename,
    out nint db);

使用[LibraryImport](.NET 7+)而不是[DllImport] – 它通过源生成器在编译时生成编组代码,使其完全AOT兼容。

DllImport vs LibraryImport

属性 AOT兼容性 编组
[DllImport] 部分 – 一些编组需要运行时代码生成 运行时编组
[LibraryImport] 完整 – 编译时源生成 编译时编组
// 从DllImport迁移到LibraryImport
// 之前:
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool CloseHandle(IntPtr hObject);

// 之后:
[LibraryImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static partial bool CloseHandle(IntPtr hObject);

本地库部署

当发布为Native AOT时,本地库(.so.dylib.dll)必须与二进制文件一起部署:

<ItemGroup>
  <!-- 在发布输出中包含本地库 -->
  <NativeLibrary Include="libs/libcustom.so" />
</ItemGroup>

大小优化

二进制大小缩减选项

<PropertyGroup>
  <PublishAot>true</PublishAot>

  <!-- 剥离调试符号(显著大小缩减) -->
  <StripSymbols>true</StripSymbols>

  <!-- 优化大小而非速度 -->
  <OptimizationPreference>Size</OptimizationPreference>

  <!-- 启用不变全球化(移除ICU数据) -->
  <InvariantGlobalization>true</InvariantGlobalization>

  <!-- 移除堆栈跟踪字符串(减少大小,更难调试) -->
  <StackTraceSupport>false</StackTraceSupport>

  <!-- 移除EventSource/EventPipe(如果不使用诊断) -->
  <EventSourceSupport>false</EventSourceSupport>
</PropertyGroup>

典型二进制大小

配置 Console App ASP.NET Minimal API
默认AOT ~10-15 MB ~15-25 MB
+ StripSymbols ~8-12 MB ~12-20 MB
+ 大小优化 ~6-10 MB ~10-18 MB
+ 不变全球化 ~4-8 MB ~8-15 MB

大小分析

# 分析什么贡献了二进制大小
dotnet publish -c Release -r linux-x64 /p:PublishAot=true

# 使用sizoscope(社区工具)进行详细大小分析
# https://github.com/AdrianEddy/sizoscope

使用runtime-deps的自包含部署

Native AOT产生自包含的二进制文件,包括.NET运行时。使用runtime-deps基础镜像以获得最小容器大小,因为运行时已经嵌入在二进制文件中。

# 构建阶段
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY . .
RUN dotnet publish -c Release -r linux-x64 -o /app/publish

# 运行时阶段 -- 使用runtime-deps,而非aspnet或runtime
FROM mcr.microsoft.com/dotnet/runtime-deps:10.0-noble-chiseled AS runtime
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["./MyApp"]

runtime-deps镜像仅包含操作系统级依赖(libc、OpenSSL等) – 无.NET运行时。这是AOT发布应用程序的最小可能基础镜像(约30 MB)。参见[技能:dotnet-containers]以获取完整容器模式。


ASP.NET Core Native AOT

Minimal API支持(.NET 8+)

ASP.NET Core Minimal APIs支持Native AOT。MVC控制器兼容AOT(它们依赖反射用于模型绑定、过滤器和路由)。

var builder = WebApplication.CreateSlimBuilder(args);

// 使用源生成的JSON上下文
builder.Services.ConfigureHttpJsonOptions(options =>
{
    options.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonContext.Default);
});

var app = builder.Build();

app.MapGet("/api/products/{id}", (int id) =>
    Results.Ok(new Product(id, "Widget")));

app.Run();

[JsonSerializable(typeof(Product))]
internal partial class AppJsonContext : JsonSerializerContext { }

record Product(int Id, string Name);

CreateSlimBuilder vs CreateBuilder

方法 AOT支持 包含内容
WebApplication.CreateSlimBuilder() 完整 最小服务,无MVC,无Razor
WebApplication.CreateBuilder() 部分 完整功能集,一些功能需要反射

对于Native AOT应用程序,使用CreateSlimBuilder。它排除需要运行时代码生成的功能。

.NET 10 ASP.NET Core AOT改进

.NET 10在ASP.NET Core和运行时Native AOT堆栈中带来改进。以net10.0为目标以自动受益。

<PropertyGroup>
  <TargetFramework>net10.0</TargetFramework>
  <PublishAot>true</PublishAot>
</PropertyGroup>

请求委托生成器改进: 为Minimal API端点创建请求委托的源生成器在.NET 10中处理更多参数绑定场景,包括额外的TypedResults返回类型和复杂绑定模式。这减少了在.NET 8/9中当生成器无法为某些端点签名生成AOT安全代码时所需的手动变通方法。

减少链接器警告表面: 许多先前发出修剪/AOT警告(IL2xxx/IL3xxx)的ASP.NET Core框架API已被注释或重构以实现AOT兼容性。从.NET 9升级到.NET 10的项目在使用PublishAot发布时将看到更少的误报链接器警告。

webapiaot模板中的OpenAPI: webapiaot项目模板现在默认包括通过Microsoft.AspNetCore.OpenApi的OpenAPI文档生成,因此AOT发布的API获得自动生成的API文档而无需额外设置。

运行时NativeAOT代码生成: .NET 10运行时改进了结构体参数的AOT代码生成,增强了循环反转优化,并改进了方法去虚拟化 – 无需代码更改即可为AOT发布的应用程序带来更好的吞吐量。

Blazor Server和SignalR: Blazor Server和SignalR在.NET 10的Native AOT中仍不支持。Blazor WebAssembly AOT(客户端编译)是一个独立关注点,由[技能:dotnet-aot-wasm]覆盖。对于Blazor Server应用程序,继续使用JIT部署。

兼容性快照(.NET 10):

功能 AOT支持
gRPC 完全支持
Minimal APIs 部分支持(大多数场景有效)
MVC 不支持
Blazor Server 不支持
SignalR 不支持
JWT身份验证 完全支持
CORS、健康检查、输出缓存 完全支持
WebSockets、静态文件 完全支持

代理陷阱

  1. 不要在库项目中使用PublishAot 库使用IsAotCompatible(自动启用AOT分析器)。PublishAot用于产生独立可执行文件的应用程序。
  2. 不要使用遗留RD.xml进行类型保留。 RD.xml是.NET Native/UWP格式,被现代.NET AOT静默忽略。使用ILLink描述符XML文件和[DynamicDependency]属性替代。
  3. 不要在新的AOT代码中使用[DllImport] 使用[LibraryImport](.NET 7+),它在编译时生成编组。[DllImport]可能需要运行时编组,这在AOT中不可用。
  4. 不要对AOT API使用WebApplication.CreateBuilder() 使用CreateSlimBuilder(),它排除反射重型功能。CreateBuilder()包括不兼容AOT的MVC基础设施。
  5. 不要使用dotnet publish --no-actual-publish进行分析。 该标志不存在。使用dotnet build /p:EnableAotAnalyzer=true /p:EnableTrimAnalyzer=true来获取诊断警告而无需发布。
  6. 不要假设MVC控制器与Native AOT一起工作。 MVC依赖反射用于模型绑定、动作过滤器和路由。对AOT发布的Web应用程序使用Minimal APIs。

参考