ASP.NET Core 8.0 introduces support for .NET native ahead-of-time (AOT). This article covers:
- How to get started.
- The benefits and potential drawbacks.
- Compatibility requirements.
Warning
In .NET 8, not all ASP.NET Core features are compatible with native AOT.
Native AOT publishing
AOT compilation happens when the app is published. Native AOT is enabled with the PublishAot
option:
1 2 3 |
<PropertyGroup> <PublishAot>true</PublishAot> </PropertyGroup> |
A project that uses native AOT publishing will use JIT compilation when debugging/running, but there are some observable differences:
- Some features that aren’t compatible with native AOT are disabled and throw exceptions at runtime.
- A source analyzer is enabled to highlight code that isn’t compatible with native AOT. At publish time, the entire app, including NuGet packages, are analyzed for compatibility again.
Native AOT analysis includes all of the app’s code and the libraries the app depends on. Review native AOT warnings and take corrective steps. It’s a good idea to test publishing apps frequently to discover issues early in the development lifecycle.
Get started
Native AOT is supported by ASP.NET Core minimal APIs and gRPC. For more information about getting started using native AOT with gRPC apps, see gRPC and native AOT.
To get started with native AOT and a minimal API, use the:
- ASP.NET Core API Application template, which includes an option to enable publishing native AOT in the new project. The AOT option includes customizations to remove unsupported components from the app.
dotnet new
command to create a new ASP.NET Core API app that is configured to work with native AOT:
1 2 3 4 5 6 7 8 9 |
$ dotnet new api -aot -o MyFirstAotWebApi && cd MyFirstAotWebApi The template "ASP.NET Core API" was created successfully. Processing post-creation actions... Restoring C:\Code\Demos\MyFirstAotWebApi\MyFirstAotWebApi.csproj: Determining projects to restore... Restored C:\Code\Demos\MyFirstAotWebApi\MyFirstAotWebApi.csproj (in 302 ms). Restore succeeded. |
Use the following command to verify an app can be published using native AOT:
1 2 3 4 5 6 7 8 9 10 11 |
$ dotnet publish MSBuild version 17.<version> for .NET Determining projects to restore... Restored C:\Code\Demos\MyFirstAotWebApi\MyFirstAotWebApi.csproj (in 241 ms). C:\Code\dotnet\aspnetcore\.dotnet\sdk\8.0.<version>\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.RuntimeIde ntifierInference.targets(287,5): message NETSDK1057: You are using a preview version of .NET. See: https://aka.ms/dotne t-support-policy [C:\Code\Demos\MyFirstAotWebApi\MyFirstAotWebApi.csproj] MyFirstAotWebApi -> C:\Code\Demos\MyFirstAotWebApi\bin\Release\net8.0\win-x64\MyFirstAotWebApi.dll Generating native code MyFirstAotWebApi -> C:\Code\Demos\MyFirstAotWebApi\bin\Release\net8.0\win-x64\publish\ |
Note: The preceding output my differ from what you see depending on the version of .NET 8 used. Review the contents of the output directory:
1 2 3 4 5 6 7 8 9 |
$ dir bin\Release\net8.0\win-x64\publish Directory: C:\Code\Demos\MyFirstAotWebApi\bin\Release\net8.0\win-x64\publish Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 30/03/2023 1:41 PM 9480704 MyFirstAotWebApi.exe -a--- 30/03/2023 1:41 PM 43044864 MyFirstAotWebApi.pdb |
The executable is self-contained and doesn’t require a .NET runtime to run. When launched it should behave as expected:
1 2 3 4 5 6 7 8 9 10 |
$ .\bin\Release\net8.0\win-x64\publish\MyFirstAotWebApi.exe info: Microsoft.Hosting.Lifetime[14] Now listening on: http://localhost:5000 info: Microsoft.Hosting.Lifetime[0] Application started. Press Ctrl+C to shut down. info: Microsoft.Hosting.Lifetime[0] Hosting environment: Production info: Microsoft.Hosting.Lifetime[0] Content root path: C:\Code\Demos\MyFirstAotWebApi |
The Program.cs
source file contains some changes for publishing to native AOT. A significant difference is that Microsoft.AspNetCore.Builder.WebApplication.CreateSlimBuilder
is used to create the web application builder. The CreateSlimBuilder
method initializes the WebApplicationBuilder with the minimal ASP.NET Core features necessary to run an application.
1 2 |
<span class="hljs-keyword">var</span> builder = WebApplication.CreateSlimBuilder(args); |
This template uses JSON to serialize responses, to enable JSON serialization with native AOT, we need to explicitly provide a JsonSerializerContext
which specifies the custom types that we need to serialize so that the JSON source generator knows what to produce code for.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
builder.Services.ConfigureHttpJsonOptions(options => { options.SerializerOptions.AddContext<AppJsonSerializerContext>(); }); <span class="hljs-comment">// Other code trimmed for brevity.</span> [<span class="hljs-meta">JsonSerializable(typeof(Todo[</span>]))] <span class="hljs-keyword">internal</span> <span class="hljs-keyword">partial</span> <span class="hljs-keyword">class</span> <span class="hljs-title">AppJsonSerializerContext</span> : <span class="hljs-title">JsonSerializerContext</span> { } |
Because unused code is trimmed during publishing for native AOT, the application cannot use unbounded reflection at runtime. Source generators are used to produce code to avoid the need for reflection. In some cases source generators produce code optimized for AOT even when a generator is not strictly required. To view source code that is generated based on the code in Program.cs
modify the MyFirstAotWebApi.csproj
to include the <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
property. Example:
1 2 3 4 5 6 7 8 9 |
<span class="hljs-tag"><<span class="hljs-name">Project</span> <span class="hljs-attr">Sdk</span>=<span class="hljs-string">"Microsoft.NET.Sdk.Web"</span>></span> <span class="hljs-tag"><<span class="hljs-name">PropertyGroup</span>></span> <span class="hljs-comment"><!-- Other properties omitted for brevity --></span> <span class="hljs-tag"><<span class="hljs-name">EmitCompilerGeneratedFiles</span>></span>true<span class="hljs-tag"></<span class="hljs-name">EmitCompilerGeneratedFiles</span>></span> <span class="hljs-tag"></<span class="hljs-name">PropertyGroup</span>></span> <span class="hljs-tag"></<span class="hljs-name">Project</span>></span> |
With the project file updated, run the dotnet build
command (publish
isn’t necessary to view generated code). In the built output file there will be a obj/Debug/net8.0/generated/
directory which contains all the generated files for the project.
When dotnet publish
is run, the project source files and generated source files are compiled as normal and then outputted assemblies are passed into an native IL compiler which produces the native executable which contains the native machine code to run the application.
Benefits of using native AOT with ASP.NET Core
Publishing and deploying a native AOT app provides the following benefits:
- Minimize disk footprint: When publishing using native AOT a single executable is produced containing just the code from external dependencies that is used to support the program. Reduced executable size can lead to:
- Smaller container images, for example in containerized deployment scenarios.
- Reduce deployment time from smaller images.
- Reduced startup time: Native AOT applications can show reduced start-up times. Reduced start-up means:
- The app is ready to service requests quicker.
- Improved deployment where container orchestrators need manage transition from one version of the app to another.
- Reduce memory demand: Native AOT apps can have reduced memory demands depending on the work being performed by the app. Reduced memory consumption can lead to greater deployment density and improved scalability.
We ran the template application in our benchmarking lab to see what the differences were in terms of application size, memory use, and CPU and observed the following results:
You can see that native AOT has a dramatically lower application size on disk and memory utilization is also lower for our template scenario. Startup time is also significantly reduced. 🚀
ASP.NET Core and native AOT compatibility
Not all features in ASP.NET Core are currently compatible with native AOT. The following table summarizes ASP.NET Core feature compatibility with native AOT:
Feature | Fully Supported | Partially Supported | Not Supported |
---|---|---|---|
gRPC | Fully supported | ||
Minimal APIs | Partially supported | ||
MVC | Not supported | ||
Blazor Server | Not supported | ||
SignalR | Not supported | ||
Authentication | Not supported (JWT soon) | ||
CORS | Fully supported | ||
HealthChecks | Fully supported | ||
HttpLogging | Fully supported | ||
Localization | Fully supported | ||
OutputCaching | Fully supported | ||
RateLimiting | Fully supported | ||
RequestDecompression | Fully supported | ||
ResponseCaching | Fully supported | ||
ResponseCompression | Fully supported | ||
Rewrite | Fully supported | ||
Session | Not supported | ||
Spa | Not supported | ||
StaticFiles | Fully supported | ||
WebSockets | Fully supported |
It is important to test your application thoroughly when moving to a native AOT deployment model to ensure that functionality observed during development (when the app is untrimmed and JIT-compiled) is preserved in the native executable. When building your application, keep an eye out for AOT warnings. An application that produces AOT warnings during publishing is not guaranteed to work correctly. If you don’t get any AOT warnings at publish time, you should be confident that your application will work consistently after publishing for AOT as it did during your F5 / dotnet run
develoment workflow.
For more information on AOT warnings and how to address them see; Introduction to AOT warnings.
Known issues
We are keeping track of a number of known issues with native AOT support in ASP.NET Core in a GitHub issue.