Compartilhar via


Configurar o Filtro 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 do .NET 10 deste artigo.

Aviso

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

Este artigo explica como controlar o Filtro de Linguagem Intermediária (IL) ao criar um aplicativo Blazor.

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

Granularidade padrão do filtro

A granularidade padrão do aparador para Blazor aplicativos é partial, o que significa que apenas as bibliotecas e bibliotecas de estrutura principal que habilitaram explicitamente o suporte de corte são cortadas. Não há suporte para corte completo.

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

Configuração

Para configurar o IL Trimmer, confira o artigo Opções de corte na documentação de Conceitos básicos do .NET, que inclui diretrizes sobre os seguintes assuntos:

  • Desabilitar o corte do aplicativo inteiro com a propriedade <PublishTrimmed> no arquivo de projeto.
  • Controlar o nível de agressividade do descarte da IL não utilizada pelo IL Trimmer.
  • Impedir que o IL Trimmer corte assemblies específicos.
  • Assemblies "raiz" para corte.
  • Avisos do Surface para tipos refletidos ao definir a propriedade <SuppressTrimAnalysisWarnings> como false no arquivo de projeto.
  • Controlar o corte de símbolos e o suporte ao depurador.
  • Definir os recursos do IL Trimmer para cortar recursos da biblioteca de estruturas.

Quando a granularidade do aparador é partial, que é o valor padrão, o IL Trimmer elimina a biblioteca de classes base e quaisquer outros assemblies marcados como removíveis. Para optar pelo corte em qualquer um dos projetos de biblioteca de classes do aplicativo, defina a propriedade MSBuild nesses projetos para <IsTrimmable>:

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

Para obter diretrizes relacionadas às bibliotecas do .NET, consulte Preparar bibliotecas do .NET para trimming.

Falha ao preservar os tipos usados por um aplicativo publicado

O corte pode ter efeitos prejudiciais para um aplicativo publicado que leva a erros de runtime, mesmo que a <PublishTrimmed> propriedadefalse seja definida no arquivo de projeto. Em aplicativos 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 os remove, ou remove os nomes dos parâmetros dos métodos. Isso pode acontecer com tipos de estrutura complexos usados para interoperabilidade JS, serialização/desserialização JSON e outras operações.

O IL Trimmer também não consegue reagir ao comportamento dinâmico de um aplicativo em runtime. 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 que a <PublishTrimmed> propriedade false seja configurada no arquivo de projeto. Acessar o 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 a adoção de uma das abordagens a seguir.

Tipos personalizados

Para evitar problemas com a redução do .NET em cenários que dependem de reflection, como interoperabilidade e serialização JSON, use tipos personalizados definidos em bibliotecas configuradas para não serem reduzidas ou preserve os tipos por meio da configuração do vinculador.

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-trimmáveis não são aparados por Blazor quando um aplicativo é publicado, o componente opera como 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, adicione uma @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 à raiz do .Client projeto de um Blazor Web App (.NET 8 ou posterior).

Adicione um TrimmerRootDescriptor item ao arquivo de projeto do aplicativo‡ referenciando o 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 propriedade MSBuild definida _ExtraTrimmerArgs no arquivo de projeto do aplicativo para preservar nomes de parâmetro durante o --keep-metadata parametername corte:

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

Recursos adicionais