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