Share via


Suporte do ASP.NET Core ao AOT nativo

O ASP.NET Core 8.0 é compatível com ahead-of-time (AOT) nativo do .NET.

Razões para usar o AOT nativo com o ASP.NET Core

Publicar e implantar um aplicativo AOT nativo oferece os seguintes benefícios:

  • Volume de disco minimizado: com a publicação por meio do AOT nativo, é produzido um só executável contendo apenas o código de dependências externas necessário para dar suporte ao programa. O tamanho do executável reduzido pode levar a:
    • Imagens de contêiner menores, por exemplo, em cenários de implantação em contêineres.
    • Tempo de implantação de imagens menores reduzido.
  • Tempo de inicialização reduzido: os aplicativos AOT nativos podem mostrar tempos de inicialização reduzidos, o que significa
    • Que o aplicativo está pronto para solicitações de serviço mais rapidamente.
    • Implantação aprimorada em que os orquestradores de contêineres precisam gerenciar a transição de uma versão do aplicativo para outra.
  • Demanda de memória reduzida: os aplicativos AOT nativos podem ter demandas de memória reduzidas, dependendo do trabalho executado pelo aplicativo. A redução do consumo de memória pode levar a maior densidade de implantação e escalabilidade aprimorada.

O aplicativo modelo foi executado em nosso laboratório de benchmarking para comparar o desempenho de um aplicativo publicado pelo AOT, um aplicativo de runtime filtrado e um aplicativo de runtime não filtrado. O gráfico a seguir mostra os resultados do benchmarking:

Chart showing comparison of application size, memory use, and startup time metrics of an AOT published app, a runtime app that is trimmed, and an untrimmed runtime app.

O gráfico anterior mostra que o AOT nativo tem tamanho de aplicativo, uso de memória e tempo de inicialização menores.

Compatibilidade entre o ASP.NET Core e o AOT nativo

Atualmente, nem todos os recursos do ASP.NET Core são compatíveis com o AOT nativo. A seguinte tabela resume a compatibilidade de recursos do ASP.NET Core com o AOT nativo:

Recurso Totalmente compatível Suporte parcial Sem suporte
gRPC Totalmente compatível
APIs mínimas Parcialmente compatível
MVC Sem suporte
Blazor Server Sem suporte
SignalR Sem suporte
Autenticação JWT Totalmente compatível
Outra autenticação Sem suporte
CORS Totalmente compatível
HealthChecks Totalmente compatível
HttpLogging Totalmente compatível
Localização Totalmente compatível
OutputCaching Totalmente compatível
RateLimiting Totalmente compatível
RequestDecompression Totalmente compatível
ResponseCaching Totalmente compatível
ResponseCompression Totalmente compatível
Rewrite Totalmente compatível
Session Sem suporte
Spa Sem suporte
StaticFiles Totalmente compatível
WebSockets Totalmente compatível

Para obter mais informações sobre limitações, consulte:

É importante testar um aplicativo minuciosamente ao migrar para um modelo de implantação AOT nativo. O aplicativo AOT implantado deve ser testado para verificar se a funcionalidade não foi alterada em relação ao aplicativo não filtrado e de compilação JIT. Ao compilar o aplicativo, analise e corrija os avisos AOT. Um aplicativo que emite avisos AOT durante a publicação pode não funcionar corretamente. Se nenhum aviso AOT for emitido no momento da publicação, o aplicativo AOT publicado deverá funcionar da mesma forma que o aplicativo compilado por untrimmed e JIT.

Publicação do AOT nativo

O AOT nativo está habilitado com a propriedade MSBuild PublishAot. O seguinte exemplo mostra como habilitar o AOT nativo em um arquivo de projeto:

<PropertyGroup>
  <PublishAot>true</PublishAot>
</PropertyGroup>

Essa configuração habilita a compilação AOT nativo durante a publicação e a análise dinâmica de uso de código durante o build e a edição. Um projeto que usa a publicação AOT nativo usa a compilação JIT ao ser executado localmente. Um aplicativo AOT tem as seguintes diferenças em relação a um aplicativo de compilação JIT:

  • Os recursos que não são compatíveis com o AOT nativo são desabilitados e geram exceções em runtime.
  • Um analisador de origem está habilitado para realçar o código que não é compatível com o AOT nativo. No momento da publicação, todo o aplicativo, incluindo pacotes NuGet, é analisado para compatibilidade novamente.

A análise AOT nativa inclui todo o código do aplicativo e as bibliotecas das quais o aplicativo depende. Leia os avisos do AOT nativo e realize as etapas corretivas. É uma boa ideia publicar aplicativos com frequência para descobrir problemas no início do ciclo de vida de desenvolvimento.

No .NET 8, o AOT nativo é compatível com os seguintes tipos de aplicativos ASP.NET Core:

O modelo de API Web (AOT nativo)

O modelo de API Web do ASP.NET Core (cujo nome curto é webapiaot) cria um projeto com o AOT habilitado. O modelo difere do modelo de projeto da API Web das seguintes maneiras:

  • Usa apenas APIs mínimas, pois o MVC ainda não é compatível com o AOT nativo.
  • Usa a API CreateSlimBuilder() para garantir que apenas os recursos essenciais sejam habilitados por padrão, minimizando o tamanho implantado do aplicativo.
  • É configurado para escutar somente HTTP, pois o tráfego HTTPS geralmente é tratado por um serviço de entrada em implantações nativas de nuvem.
  • Não inclui um perfil de inicialização para execução no IIS ou no IIS Express.
  • Cria um arquivo .http configurado com solicitações HTTP de exemplo que podem ser enviadas para os pontos de extremidade do aplicativo.
  • Inclui uma amostra de API Todo em vez da amostra de previsão do tempo.
  • Adiciona PublishAot ao arquivo de projeto, conforme mostrado anteriormente neste artigo.
  • Habilita os geradores de origem do serializador JSON. O gerador de origem é usado para gerar um código de serialização no momento do build, o que é necessário para a compilação AOT nativo.

Alterações para dar suporte à geração de origem

O exemplo a seguir mostra o código adicionado ao arquivo Program.cs para dar suporte à geração de origem de serialização JSON:

using MyFirstAotWebApi;
+using System.Text.Json.Serialization;

-var builder = WebApplication.CreateBuilder();
+var builder = WebApplication.CreateSlimBuilder(args);

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

var app = builder.Build();

var sampleTodos = TodoGenerator.GenerateTodos().ToArray();

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();

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

Sem esse código adicionado, System.Text.Json usa a reflexão para serializar e desserializar o JSON. Não há suporte para reflexão no AOT nativo.

Para saber mais, veja:

Muda para launchSettings.json

O arquivo launchSettings.json criado pelo modelo de API Web (AOT nativo) tem a seção IIS Express e o perfil iisSettings removidos:

{
  "$schema": "http://json.schemastore.org/launchsettings.json",
-  "iisSettings": {
-     "windowsAuthentication": false,
-     "anonymousAuthentication": true,
-     "iisExpress": {
-       "applicationUrl": "http://localhost:11152",
-       "sslPort": 0
-     }
-   },
  "profiles": {
    "http": {
      "commandName": "Project",
      "dotnetRunMessages": true,
      "launchBrowser": true,
      "launchUrl": "todos",
      "applicationUrl": "http://localhost:5102",
        "environmentVariables": {
          "ASPNETCORE_ENVIRONMENT": "Development"
        }
      },
-     "IIS Express": {
-       "commandName": "IISExpress",
-       "launchBrowser": true,
-       "launchUrl": "todos",
-      "environmentVariables": {
-       "ASPNETCORE_ENVIRONMENT": "Development"
-      }
-    }
  }
}

O método CreateSlimBuilder

O modelo usa o método CreateSlimBuilder() em vez do método CreateBuilder().

using System.Text.Json.Serialization;
using MyFirstAotWebApi;

var builder = WebApplication.CreateSlimBuilder(args);
builder.Logging.AddConsole();

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

var app = builder.Build();

var sampleTodos = TodoGenerator.GenerateTodos().ToArray();

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();

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

}

O método CreateSlimBuilder inicializa o WebApplicationBuilder com os recursos mínimos do ASP.NET Core necessários para executar um aplicativo.

Conforme observado anteriormente, o método CreateSlimBuilder não é compatível com HTTPS ou HTTP/3. Normalmente, esses protocolos não são necessários para aplicativos executados por trás de um proxy de terminação TLS. Por exemplo, veja a Terminação TLS e TLS de ponta a ponta com o Gateway de Aplicativo. O HTTPS pode ser habilitado chamando builder.WebHost.UseKestrelHttpsConfiguration. O HTTP/3 pode ser habilitado chamando builder.WebHost.UseQuic.

CreateSlimBuilder versus CreateBuilder

O método CreateSlimBuilder não dá suporte aos seguintes recursos que têm suporte pelo método CreateBuilder:

O método CreateSlimBuilder inclui os seguintes recursos necessários para uma experiência de desenvolvimento eficiente:

  • Configuração de arquivo JSON para appsettings.json e appsettings.{EnvironmentName}.json.
  • Configuração de segredos do usuário.
  • Registro em log de console.
  • Configuração do registro em log.

Para um construtor que omite os recursos acima, consulte O método CreateEmptyBuilder.

A inclusão de recursos mínimos tem benefícios para filtragem, bem como AOT. Para obter mais informações, confira Cortar implantações e executáveis autocontidos.

Para obter informações mais detalhadas, consulte Comparando WebApplication.CreateBuilder com CreateSlimBuilder

Geradores de origem

Como o código não utilizado é cortado durante a publicação para o AOT nativo, o aplicativo não pode usar a reflexão desassociada em runtime. Os geradores de origem são usados para produzir código que evita a necessidade de reflexão. Em alguns casos, os geradores de origem produzem código otimizado para AOT mesmo quando um gerador não é necessário.

Para exibir o código-fonte gerado, adicione a propriedade EmitCompilerGeneratedFiles ao arquivo .csproj de um aplicativo, conforme mostrado no exemplo a seguir:

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

  <PropertyGroup>
    <!-- Other properties omitted for brevity -->
    <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
  </PropertyGroup>

</Project>

Execute o comando dotnet build para ver o código gerado. A saída inclui um diretório obj/Debug/net8.0/generated/ que contém todos os arquivos gerados para o projeto.

O comando dotnet publish também compila os arquivos de origem e gera arquivos compilados. Além disso, dotnet publish passa os assemblies gerados para um compilador de IL nativo. O compilador de IL produz o executável nativo. O executável nativo contém o código do computador nativo.

Bibliotecas e AOT nativo

Atualmente, muitas das bibliotecas populares usadas em projetos do ASP.NET Core trazem alguns problemas de compatibilidade quando usadas em um projeto direcionado ao AOT nativo, como:

  • Uso de reflexão para inspecionar e descobrir tipos.
  • Carregamento condicional de bibliotecas em runtime.
  • Geração de código em tempo real para implementar a funcionalidade.

As bibliotecas que usam esses recursos dinâmicos precisam ser atualizadas para funcionar com o AOT nativo. Elas podem ser atualizadas usando ferramentas como geradores de origem Roslyn.

Os autores da biblioteca que desejam dar suporte ao AOT nativo são incentivados a:

APIs mínimas e cargas JSON

A estrutura de API Mínima é otimizada para receber e retornar cargasJSON usando System.Text.Json. System.Text.Json:

Todos os tipos transmitidos como parte do corpo HTTP ou retornados de representantes de solicitação em aplicativos de APIs Mínimas devem ser configurados em um JsonSerializerContext registrado por meio da injeção de dependência do ASP.NET Core:

using System.Text.Json.Serialization;
using MyFirstAotWebApi;

var builder = WebApplication.CreateSlimBuilder(args);
builder.Logging.AddConsole();

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

var app = builder.Build();

var sampleTodos = TodoGenerator.GenerateTodos().ToArray();

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();

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

}

No código realçado anterior:

Um parâmetro no delegado que não está associado ao corpo e não precisa ser serializável. Por exemplo, um parâmetro de cadeia de caracteres de consulta que é um tipo de objeto avançado e implementa IParsable<T>.

public class Todo
{
    public int Id { get; set; }
    public string? Title { get; set; }
    public DateOnly? DueBy { get; set; }
    public bool IsComplete { get; set; }
}

static class TodoGenerator
{
    private static readonly (string[] Prefixes, string[] Suffixes)[] _parts = new[]
        {
            (new[] { "Walk the", "Feed the" }, new[] { "dog", "cat", "goat" }),
            (new[] { "Do the", "Put away the" }, new[] { "groceries", "dishes", "laundry" }),
            (new[] { "Clean the" }, new[] { "bathroom", "pool", "blinds", "car" })
        };
    // Remaining code omitted for brevity.

Problemas conhecidos

Confira este problema do GitHub para relatar ou analisar problemas com o suporte do AOT nativo no ASP.NET Core.

Veja também