Partilhar via


Configurar o Trimmer para ASP.NET Core Blazor

Observação

Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão .NET 10 deste artigo.

Advertência

Esta versão do ASP.NET Core não é mais suportada. Para obter mais informações, consulte a Política de suporte do .NET e .NET Core. Para a versão atual, consulte a versão .NET 10 deste artigo.

Este artigo explica como controlar o Intermediate Language (IL) Trimmer ao criar um Blazor aplicativo.

Blazor WebAssembly executa o corte de linguagem intermediária (IL) para reduzir o tamanho da saída publicada. O corte ocorre ao publicar um aplicativo.

Granularidade padrão do trimmer

A granularidade padrão do trimmer para Blazor aplicativos é partial, o que significa que apenas as bibliotecas da estrutura principal e as bibliotecas que habilitaram explicitamente o suporte à filtragem são cortadas. O corte total não é suportado.

Para obter mais informações, consulte Opções de corte (documentação do .NET).

Configuração

Para configurar o IL Trimmer, consulte o artigo Opções de corte na documentação do .NET Fundamentals, que inclui orientações sobre os seguintes assuntos:

  • Desative a eliminação para toda a aplicação com a propriedade <PublishTrimmed> no ficheiro de projeto.
  • Controle quão agressivamente a IL não utilizada é eliminada pelo IL Trimmer.
  • Impeça o IL Trimmer de cortar montagens específicas.
  • Montagens "raiz" para corte.
  • Exiba avisos para tipos de dados refletidos configurando a propriedade <SuppressTrimAnalysisWarnings> para false no ficheiro do projeto.
  • Controle a remoção de símbolos e o suporte para depuração.
  • Defina os recursos do IL Trimmer para cortar os recursos da biblioteca da estrutura.

Quando a granularidade do trimmer é partial, que é o valor padrão, o IL Trimmer corta a biblioteca de classes base e quaisquer outros assemblies marcados como trimmable. Para optar por cortar em qualquer um dos projetos de biblioteca de classes do aplicativo, defina a <IsTrimmable> propriedade MSBuild como true nesses projetos:

<PropertyGroup>
  <IsTrimmable>true</IsTrimmable>
</PropertyGroup>

Para obter orientações relacionadas às bibliotecas .NET, consulte Preparar bibliotecas .NET para corte.

Falha ao preservar os tipos usados por um aplicativo publicado

O corte pode ter efeitos prejudiciais para um aplicativo publicado, levando a erros de tempo de execução, mesmo apesar de definir a <PublishTrimmed> propriedade como false no arquivo de projeto. Em aplicações que usam reflexão, o IL Trimmer muitas vezes não consegue determinar os tipos necessários para reflexão em tempo de execução e remove-os ou remove os nomes dos parâmetros dos métodos. Isso pode acontecer com tipos de frameworks complexos usados para JS interoperabilidade, serialização/desserialização JSON e para outras operações.

O IL Trimmer também não consegue reagir ao comportamento dinâmico de um aplicativo em tempo de execução. Para garantir que o aplicativo cortado funcione corretamente depois de implantado, teste a saída publicada com frequência durante o desenvolvimento.

Considere o exemplo a seguir que executa a desserialização JSON em uma Tuple<T1,T2> coleção (List<Tuple<string, string>>).

TrimExample.razor:

@page "/trim-example"
@using System.Diagnostics.CodeAnalysis
@using System.Text.Json

<h1>Trim Example</h1>

<ul>
    @foreach (var item in @items)
    {
        <li>@item.Item1, @item.Item2</li>
    }
</ul>

@code {
    private List<Tuple<string, string>> items = [];

    [StringSyntax(StringSyntaxAttribute.Json)]
    private const string data =
        """[{"item1":"1:T1","item2":"1:T2"},{"item1":"2:T1","item2":"2:T2"}]""";

    protected override void OnInitialized()
    {
        JsonSerializerOptions options = new() { PropertyNameCaseInsensitive = true };

        items = JsonSerializer
            .Deserialize<List<Tuple<string, string>>>(data, options)!;
    }
}

O componente anterior é executado normalmente quando o aplicativo é executado localmente e produz a seguinte lista renderizada:

• 1:T1, 1:T2
• 2:T2, 2:T2

Quando o aplicativo é publicado, Tuple<T1,T2> é cortado do aplicativo, mesmo apesar de definir a <PublishTrimmed> propriedade como false no arquivo de projeto. O acesso ao componente gera a seguinte exceção:

crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
Unhandled exception rendering component: ConstructorContainsNullParameterNames, System.Tuple`2[System.String,System.String]
System.NotSupportedException: ConstructorContainsNullParameterNames, System.Tuple`2[System.String,System.String]

Para lidar com tipos perdidos, considere adotar uma das seguintes abordagens.

Tipos personalizados

Para evitar problemas com a eliminação do .NET em cenários que dependem de reflexão de código, como interoperabilidade e serialização JSON, use tipos personalizados definidos em bibliotecas não elimináveis ou preserve os tipos por meio de configuração do linker.

As modificações a seguir criam um StringTuple tipo para uso pelo componente.

StringTuple.cs:

[method: SetsRequiredMembers]
public sealed class StringTuple(string item1, string item2)
{
    public required string Item1 { get; init; } = item1;
    public required string Item2 { get; init; } = item2;
}

O componente é modificado para usar o StringTuple tipo:

- private List<Tuple<string, string>> items = [];
+ private List<StringTuple> items = [];
- items = JsonSerializer.Deserialize<List<Tuple<string, string>>>(data, options)!;
+ items = JsonSerializer.Deserialize<List<StringTuple>>(data, options)!;

Como os tipos personalizados definidos em assemblies não removíveis não são eliminados por Blazor durante a publicação de um aplicativo, o componente funciona conforme projetado após a publicação do aplicativo.

Se você preferir usar tipos de estrutura apesar de nossa recomendação, use uma das seguintes abordagens:

Se você preferir usar tipos de estrutura apesar de nossa recomendação, preserve o tipo como uma dependência dinâmica.

Preservar o tipo como uma dependência dinâmica

Crie uma dependência dinâmica para preservar o tipo com o [DynamicDependency] atributo.

Se ainda não estiver presente, adicionar a @using diretiva para System.Diagnostics.CodeAnalysis.

@using System.Diagnostics.CodeAnalysis

Adicione um [DynamicDependency] atributo para preservar o Tuple<T1,T2>:

+ [DynamicDependency(DynamicallyAccessedMemberTypes.PublicConstructors, 
+     typeof(Tuple<string, string>))]
private List<Tuple<string, string>> items = [];

Usar um descritor raiz

Um descritor raiz pode preservar o tipo.

Adicione um ILLink.Descriptors.xml arquivo à raiz do aplicativo† com o tipo:

<linker>
  <assembly fullname="System.Private.CoreLib">
    <type fullname="System.Tuple`2" preserve="all" />
  </assembly>
</linker>

†A raiz do aplicativo refere-se à raiz do Blazor WebAssembly aplicativo ou à .Client raiz do projeto de um Blazor Web App (.NET 8 ou posterior).

Adicione um TrimmerRootDescriptor item ao arquivo de projeto do aplicativo‡ fazendo referência ao ILLink.Descriptors.xml arquivo:

<ItemGroup>
  <TrimmerRootDescriptor Include="$(MSBuildThisFileDirectory)ILLink.Descriptors.xml" />
</ItemGroup>

‡O arquivo de projeto é o arquivo de projeto do Blazor WebAssembly aplicativo ou o arquivo de projeto do .Client projeto de um Blazor Web App (.NET 8 ou posterior).

Solução alternativa no .NET 8

Como uma solução alternativa no .NET 8, você pode adicionar a _ExtraTrimmerArgs propriedade MSBuild definida como --keep-metadata parametername no arquivo de projeto do aplicativo para preservar nomes de parâmetros durante o corte:

<PropertyGroup>
  <_ExtraTrimmerArgs>--keep-metadata parametername</_ExtraTrimmerArgs>
</PropertyGroup>

Recursos adicionais