次の方法で共有


ASP.NET Core Blazor 用のトリマーを構成する

注意

これは、この記事の最新バージョンではありません。 現在のリリースについては、 この記事の .NET 10 バージョンを参照してください。

警告

このバージョンの ASP.NET Core はサポート対象から除外されました。 詳細については、 .NET および .NET Core サポート ポリシーを参照してください。 現在のリリースについては、この記事の .NET 9 バージョンを参照してください。

この記事では、Blazor アプリを構築するときに、中間言語 (IL) トリマーを制御する方法について説明します。

Blazor WebAssembly では、発行された出力のサイズを縮小するために、中間言語 (IL) トリミングが実行されます。 トリミングはアプリの発行時に行われます。

既定のトリマーの細分性

Blazor アプリの既定のトリマ粒度はpartialです。つまり、トリミングのサポートを明示的に有効にしたコア フレームワーク ライブラリとライブラリのみがトリミングされます。 完全トリミングはサポートされていません。

詳細については、「トリミング オプション (.NET ドキュメント)」を参照してください。

構成

IL トリマーを構成するには、.NET の基礎ドキュメントのトリミング オプションに関する記事を参照してください。次の項目に関するガイダンスが含まれています。

  • プロジェクト ファイルの <PublishTrimmed> プロパティを使用し、アプリ全体のトリミングを無効にします。
  • 使用されていない IL を IL トリマーによって破棄する積極度を制御します。
  • 特定のアセンブリのトリミングを IL トリマーに禁止します。
  • トリミング用の "ルート" アセンブリ。
  • プロジェクト ファイルで <SuppressTrimAnalysisWarnings> プロパティを false に設定することで、リフレクションされた型の警告を表示します。
  • シンボル トリミングとデバッガー サポートを制御します。
  • フレームワーク ライブラリ機能をトリミングするための IL トリマー機能を設定します。

トリマの粒度が既定値であるpartial場合、IL トリマーは基底クラス ライブラリとその他のアセンブリをトリミング可能としてマークします。 アプリのクラス ライブラリ プロジェクトでトリミングをオプトインするには、 <IsTrimmable> MSBuild プロパティをこれらのプロジェクトで true するように設定します。

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

.NET ライブラリに関連するガイダンスについては、「 トリミングのための .NET ライブラリの準備」を参照してください。

発行されたアプリで使用される型を保持できない

プロジェクト ファイルで <PublishTrimmed>に設定したにもかかわらず、トリミングは発行されたアプリに悪影響を及ぼす可能性があり、実行時エラーが発生する可能性があります。 リフレクションを使用するアプリでは、多くの場合、IL トリマーはランタイム リフレクションに必要な型を特定して、それらをトリミングしたり、メソッドからパラメーター名を削除したりできません。 これは、 JS 相互運用、JSON シリアル化/逆シリアル化、およびその他の操作に使用される複雑なフレームワーク型で発生する可能性があります。

IL トリマーは、実行時にアプリの動的な動作に反応することもできません。 展開後、トリミングされたアプリが確実に正しく機能するよう、発行された出力を開発中に頻繁にテストしてください。

Tuple<T1,T2> コレクション (List<Tuple<string, string>>) への JSON 逆シリアル化を実行する次の例を考えてみましょう。

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)!;
    }
}

上記のコンポーネントは、アプリがローカルで実行され、次のレンダリングされたリストが生成されるときに通常実行されます。

• 1:T1、1:T2
• 2:T2、2:T2

アプリが発行されると、Tuple<T1,T2> プロパティをプロジェクト ファイルに<PublishTrimmed>に設定したにもかかわらず、falseはアプリからトリミングされます。 コンポーネントにアクセスすると、次の例外がスローされます。

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]

失われた型に対処するには、次のいずれかの方法を採用することを検討してください。

カスタム型

JS相互運用機能や JSON シリアル化など、リフレクションに依存するシナリオでの .NET トリミングの問題を回避するには、トリミングできないライブラリで定義されているカスタム型を使用するか、リンカー構成を使用して型を保持します。

次の変更により、コンポーネントで使用する StringTuple 型が作成されます。

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;
}

コンポーネントは、 StringTuple 型を使用するように変更されます。

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

トリミングできないアセンブリで定義されているカスタム型は、アプリの発行時に Blazor によってトリミングされないため、コンポーネントはアプリの発行後に設計どおりに動作します。

推奨されているにもかかわらずフレームワークの種類を使用する場合は、次のいずれかの方法を使用します。

推奨事項にもかかわらずフレームワーク型を使用する場合は、 型を動的な依存関係として保持します

型を動的依存関係として保持する

動的依存関係を作成して、 [DynamicDependency] 属性を持つ型を保持します。

まだ存在しない場合は、@using ディレクティブをSystem.Diagnostics.CodeAnalysis用に追加します。

@using System.Diagnostics.CodeAnalysis

[DynamicDependency]属性を追加して、Tuple<T1,T2>を保持します。

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

ルート記述子を使用する

ルート記述子は、型を保持できます。

ILLink.Descriptors.xml ファイルをアプリのルートに追加します†次のように入力します。

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

†アプリのルートは、Blazor WebAssembly アプリのルート、または.Client (.NET 8 以降) のBlazor Web App プロジェクトのルートを参照します。

TrimmerRootDescriptor ファイルを参照する‡アプリのプロジェクト ファイルにILLink.Descriptors.xml項目を追加します。

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

‡プロジェクト ファイルは、Blazor WebAssembly アプリのプロジェクト ファイルか、.Client (.NET 8 以降) のBlazor Web App プロジェクトのプロジェクト ファイルです。

.NET 8 の回避策

.NET 8 の回避策として、 _ExtraTrimmerArgs MSBuild プロパティをアプリのプロジェクト ファイルで --keep-metadata parametername に設定して、トリミング中にパラメーター名を保持することができます。

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

その他のリソース