How to use native AOT to improve performance of ASP.NET Core apps

September 30, 2023 by Anuraj

dotnet aot

This post is about explains what native AOT is and why it is beneficial for ASP.NET Core apps. Native AOT is a deployment model that uses an ahead-of-time compiler to compile IL to native code at the time of publish. Native AOT apps have faster startup time, smaller memory footprints, and can run on machines that don’t have the .NET runtime installed. Please note all the ASP.NET Core features are not currently compatible with native AOT. For instance, ASP.NET Core application types like minimal APIs, gRPC, and worker services are compatible with native AOT, while features such as MVC, Blazor Server, and SignalR are not supported in AOT compilation.

In this blog post I am using .NET 8.0 RC version. To create the Minimal API project with AOT, we can run the following command - dotnet new webapiaot -o HelloWorldApi - this will create a Web API project with AOT enabled. If we look into the project file, we will be able to see a new element - PublishAot it should be set to true to enable AOT, like this.

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

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <InvariantGlobalization>true</InvariantGlobalization>
    <PublishAot>true</PublishAot>
  </PropertyGroup>

</Project>

And in the Minimal API code, we will be able to see few more changes like the following.

builder.Services.ConfigureHttpJsonOptions(options =>
{
    options.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonSerializerContext.Default);
});

By inserting AppJsonSerializerContext.Default into the TypeInfoResolverChain, we are telling System.Text.Json to use the source-generated code for serializing and deserializing Todo[] instances, instead of relying on runtime reflection or dynamic code generation. This way, we can use System.Text.Json in .NET AOT without losing any functionality.

[JsonSerializable(typeof(Todo[]))]
internal partial class AppJsonSerializerContext : JsonSerializerContext
{

}

To use the source generator, we need to implement a JsonSerializerContext class, which is an abstract class that provides information about the types that can be serialized or deserialized by System.Text.Json. You can annotate this class with attributes to specify the types and options for source generation. The source generator will then generate a partial class that implements the abstract methods of JsonSerializerContext.

The .NET AOT uses WebApplication.CreateSlimBuilder - The advantage of using WebApplication.CreateSlimBuilder(args) is that it creates a WebApplicationBuilder instance with minimal configuration and dependencies, which can improve the performance and startup time of your web application. WebApplication.CreateSlimBuilder(args) is a new method in ASP.NET Core 8 that simplifies the creation of web applications using Minimal APIs.

Here is complete Web API code generated by the template.

using System.Text.Json.Serialization;

var builder = WebApplication.CreateSlimBuilder(args);

builder.Services.ConfigureHttpJsonOptions(options =>
{
    options.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonSerializerContext.Default);
});

var app = builder.Build();

var sampleTodos = new Todo[] {
    new(1, "Walk the dog"),
    new(2, "Do the dishes", DateOnly.FromDateTime(DateTime.Now)),
    new(3, "Do the laundry", DateOnly.FromDateTime(DateTime.Now.AddDays(1))),
    new(4, "Clean the bathroom"),
    new(5, "Clean the car", DateOnly.FromDateTime(DateTime.Now.AddDays(2)))
};

var todosApi = app.MapGroup("/todos");
todosApi.MapGet("/", () => sampleTodos);
todosApi.MapGet("/{id}", (int id) =>
    sampleTodos.FirstOrDefault(a => a.Id == id) is { } todo
        ? Results.Ok(todo)
        : Results.NotFound());

app.Run();

public record Todo(int Id, string? Title, DateOnly? DueBy = null, bool IsComplete = false);

[JsonSerializable(typeof(Todo[]))]
internal partial class AppJsonSerializerContext : JsonSerializerContext
{

}

We can create the native AOT project using the Create a new project wizard, like this.

Static Web App running

For more information about the native AOT implementation on ASP.NET Core - Native AOT deployment

Happy Programming.

Copyright © 2024 Anuraj. Blog content licensed under the Creative Commons CC BY 2.5 | Unless otherwise stated or granted, code samples licensed under the MIT license. This is a personal blog. The opinions expressed here represent my own and not those of my employer. Powered by Jekyll. Hosted with ❤ by GitHub