ASP.NET Core でのアセンブリの遅延読み込みBlazor WebAssembly

Blazor WebAssembly アプリケーションの起動パフォーマンスは、アセンブリが必要になるまでアプリケーション アセンブリの読み込みを待機することで改善できます。これは "遅延読み込み" と呼ばれます。

この記事の初めのセクションでは、アプリの構成について説明します。 実際の動作のデモンストレーションについては、この記事の最後にある「コード例全体」のセクションを参照してください。

この記事は Blazor WebAssembly アプリにのみ該当します。 Blazor Server アプリでは、アセンブリの遅延読み込みを行っても効果がありません。これは、Blazor Server アプリのアセンブリがクライアントにダウンロードされないためです。

プロジェクト ファイルの構成

アプリのプロジェクト ファイル (.csproj) 内で、BlazorWebAssemblyLazyLoad 項目を使用して、遅延読み込みのマークをアセンブリに付けます。 .dll 拡張子が含まれるアセンブリ名を使用します。 Blazor フレームワークを使用すると、アプリの起動時にアセンブリは読み込まれません。

<ItemGroup>
  <BlazorWebAssemblyLazyLoad Include="{ASSEMBLY NAME}.dll" />
</ItemGroup>

{ASSEMBLY NAME} プレースホルダーは、アセンブリの名前です。 .dll ファイル拡張子が必要です。

アセンブリごとに 1 個の BlazorWebAssemblyLazyLoad 項目を含めます。 アセンブリに依存関係がある場合は、依存関係ごとに BlazorWebAssemblyLazyLoad エントリを含めます。

Router コンポーネントの構成

Blazor フレームワークを使用すると、クライアント側の Blazor WebAssembly アプリ† である LazyAssemblyLoader で、アセンブリの遅延読み込みを行うシングルトン サービスが自動的に登録されます。 LazyAssemblyLoader.LoadAssembliesAsync メソッド:

  • JS 相互運用を使用して、ネットワーク呼び出しを介してアセンブリをフェッチします。
  • ブラウザー内の WebAssembly で実行されているランタイムにアセンブリを読み込みます。

†"ホストされた" Blazor WebAssemblyソリューションに関するガイダンスについては、「ホストされた Blazor WebAssembly ソリューションでのアセンブリの遅延読み込み」のセクションを参照してください。

Blazor の Router は、Blazor がルーティング可能なコンポーネントを求めて探索するアセンブリを指定し、ユーザーが移動するルートのコンポーネントをレンダリングする役割も担うコンポーネントです。 Router コンポーネントの OnNavigateAsync メソッドは、ユーザーが要求するエンドポイント用の正しいアセンブリを読み込むために、遅延読み込みと組み合わせて使用されます。

LazyAssemblyLoader を使用して読み込むアセンブリを決定するロジックは、OnNavigateAsync 内に実装されています。 ロジックを構成する方法のオプションは次のとおりです。

  • OnNavigateAsync メソッド内での条件チェック。
  • ルートをアセンブリ名にマップするルックアップ テーブル (コンポーネントに挿入されるか、@code ブロック内に実装される)。

次に例を示します。

  • Microsoft.AspNetCore.Components.WebAssembly.Services の名前空間が指定されています。
  • LazyAssemblyLoader サービスが挿入されます (AssemblyLoader)。
  • {PATH} プレースホルダーは、アセンブリのリストを読み込む場所のパスです。 この例では、1 つのアセンブリ セットを読み込む 1 つのパスの条件付きチェックを使用しています。
  • {LIST OF ASSEMBLIES} プレースホルダーは、.dll 拡張子 (たとえば、"Assembly1.dll", "Assembly2.dll") を含めたアセンブリのファイル名文字列のコンマ区切りのリストです。

App.razor:

@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.WebAssembly.Services
@using Microsoft.Extensions.Logging
@inject LazyAssemblyLoader AssemblyLoader
@inject ILogger<App> Logger

<Router AppAssembly="@typeof(App).Assembly" 
    OnNavigateAsync="@OnNavigateAsync">
    ...
</Router>

@code {
    private async Task OnNavigateAsync(NavigationContext args)
    {
        try
           {
               if (args.Path == "{PATH}")
               {
                   var assemblies = await AssemblyLoader.LoadAssembliesAsync(
                       new[] { {LIST OF ASSEMBLIES} });
               }
           }
           catch (Exception ex)
           {
               Logger.LogError("Error: {Message}", ex.Message);
           }
    }
}

注意

前の例には、Router コンポーネントの Razor マークアップ (...) の内容が示されていません。 完全なコードのデモンストレーションについては、この記事の「コード例全体」のセクションを参照してください。

ルーティング可能なコンポーネントを含むアセンブリ

アセンブリの一覧にルーティング可能なコンポーネントが含まれている場合は、指定されたパスのアセンブリ リストが Router コンポーネントの AdditionalAssemblies コレクションに渡されます。

次に例を示します。

  • lazyLoadedAssembliesList<Assembly> で AdditionalAssemblies にアセンブリ リストが渡されます。 フレームワークにおいてルートがアセンブリ内で検索され、新しいルートが見つかった場合はルート コレクションが更新されます。 Assembly 型にアクセスするために、System.Reflection の名前空間が App.razor ファイルの先頭に含まれています。
  • {PATH} プレースホルダーは、アセンブリのリストを読み込む場所のパスです。 この例では、1 つのアセンブリ セットを読み込む 1 つのパスの条件付きチェックを使用しています。
  • {LIST OF ASSEMBLIES} プレースホルダーは、.dll 拡張子 (たとえば、"Assembly1.dll", "Assembly2.dll") を含めたアセンブリのファイル名文字列のコンマ区切りのリストです。

App.razor:

@using System.Reflection
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.WebAssembly.Services
@using Microsoft.Extensions.Logging
@inject LazyAssemblyLoader AssemblyLoader
@inject ILogger<App> Logger

<Router AppAssembly="@typeof(App).Assembly" 
    AdditionalAssemblies="@lazyLoadedAssemblies" 
    OnNavigateAsync="@OnNavigateAsync">
    ...
</Router>

@code {
    private List<Assembly> lazyLoadedAssemblies = new();

    private async Task OnNavigateAsync(NavigationContext args)
    {
        try
           {
               if (args.Path == "{PATH}")
               {
                   var assemblies = await AssemblyLoader.LoadAssembliesAsync(
                       new[] { {LIST OF ASSEMBLIES} });
                   lazyLoadedAssemblies.AddRange(assemblies);
               }
           }
           catch (Exception ex)
           {
               Logger.LogError("Error: {Message}", ex.Message);
           }
    }
}

注意

前の例には、Router コンポーネントの Razor マークアップ (...) の内容が示されていません。 完全なコードのデモンストレーションについては、この記事の「コード例全体」のセクションを参照してください。

詳細については、「ASP.NET Core の Blazor ルーティングとナビゲーション」を参照してください。

<Navigating> コンテンツとのユーザー操作

アセンブリの読み込み中 (数秒かかることがある)、Router コンポーネントで Navigating プロパティを使用して、ページの切り替えが行われていることをユーザーに示すことができます。

詳細については、「ASP.NET Core の Blazor ルーティングとナビゲーション」を参照してください。

OnNavigateAsync でキャンセルを処理する

OnNavigateAsync コールバックに渡される NavigationContext オブジェクトには、新しいナビゲーション イベントが発生したときに設定される CancellationToken が含まれています。 そのキャンセル トークンが、古いナビゲーションに対して OnNavigateAsync コールバックを継続して実行しないように設定されている場合は、OnNavigateAsync コールバックをスローする必要があります。

詳細については、「ASP.NET Core の Blazor ルーティングとナビゲーション」を参照してください。

OnNavigateAsync イベントと名前が変更されたアセンブリ ファイル

リソース ローダーでは、blazor.boot.json ファイルで定義されているアセンブリ名が使用されます。 アセンブリの名前が変更された場合、OnNavigateAsync コールバックで使用されるアセンブリ名と blazor.boot.json ファイル内のアセンブリ名が同期されなくなります。

これを修正するには:

  • 使用するアセンブリ名を決定するときに、アプリが Production 環境で実行されているかどうかを確認します。
  • 名前を変更したアセンブリ名を別のファイルに格納し、そのファイルから読み取りを行い、LazyAssemblyLoader サービスおよび OnNavigateAsync コールバックで使用するアセンブリ名を決定します。

ホストされた Blazor WebAssembly ソリューションでのアセンブリの遅延読み込み

フレームワークの遅延読み込みの実装では、ホストされた Blazor WebAssemblyソリューションでのプリレンダリングによる遅延読み込みがサポートされます。 プリレンダリング中は、遅延読み込みのマークが付けられたものも含め、すべてのアセンブリが読み込まれると見なされます。 Server プロジェクトに LazyAssemblyLoader サービスを手動で登録します。

Server プロジェクトの Program.cs ファイルの先頭に、Microsoft.AspNetCore.Components.WebAssembly.Services の名前空間を追加します。

using Microsoft.AspNetCore.Components.WebAssembly.Services;

Server プロジェクトの Program.cs で、サービスを登録します。

builder.Services.AddScoped<LazyAssemblyLoader>();

コード例全体

このセクションのデモンストレーションの内容は次のとおりです。

  • Robot コンポーネント (/robot のルート テンプレートを使用した Robot.razor) を含む Razor クラス ライブラリ (RCL) としてロボット コントロール アセンブリ (GrantImaharaRobotControls.dll) を作成します。
  • ユーザーから Robot の URL が要求されたときに /robot コンポーネントをレンダリングするために、RCL のアセンブリを遅延読み込みします。
  1. 新しい ASP.NET Core クラス ライブラリ プロジェクトを作成します。

    • Visual Studio: [ソリューションの作成]>[新しいプロジェクトの作成]>Razor [クラス ライブラリ] 。 プロジェクトに GrantImaharaRobotControls という名前を付けます。
    • Visual Studio Code/.NET CLI: コマンド プロンプトから dotnet new razorclasslib -o GrantImaharaRobotControls を実行します。 -o|--output オプションでソリューションのフォルダーを作成し、プロジェクトに GrantImaharaRobotControls という名前を付けます。
  2. このセクションの後半に示すコンポーネント例では、Blazor フォームを使用します。 RCL プロジェクトに、Microsoft.AspNetCore.Components.Forms パッケージを追加します。

    Note

    .NET アプリへのパッケージの追加に関するガイダンスについては、「パッケージ利用のワークフロー」 (NuGet ドキュメント) の "パッケージのインストールと管理" に関する記事を参照してください。 NuGet.org で正しいパッケージ バージョンを確認します。

  3. ロボットに親指を立てるジェスチャを実行させると仮定した ThumbUp メソッドを使用して、RCL に HandGesture クラスを作成します。 そのメソッドに軸の引数 (Left または Right) を enum として渡します。 メソッドが正常に終了すると、true が返されます。

    HandGesture.cs:

    using Microsoft.Extensions.Logging;
    
    namespace GrantImaharaRobotControls
    {
        public static class HandGesture
        {
            public static bool ThumbUp(Axis axis, ILogger logger)
            {
                logger.LogInformation("Thumb up gesture. Axis: {Axis}", axis);
    
                // Code to make robot perform gesture
    
                return true;
            }
        }
    
        public enum Axis { Left, Right }
    }
    
  4. 次のコンポーネントを RCL プロジェクトのルートに追加します。 このコンポーネントでユーザーは左手または右手の親指を立てるジェスチャ要求を送信できます。

    Robot.razor:

    @page "/robot"
    @using Microsoft.AspNetCore.Components.Forms
    @using Microsoft.Extensions.Logging
    @inject ILogger<Robot> Logger
    
    <h1>Robot</h1>
    
    <EditForm Model="@robotModel" OnValidSubmit="@HandleValidSubmit">
        <InputRadioGroup @bind-Value="robotModel.AxisSelection">
            @foreach (var entry in (Axis[])Enum
                .GetValues(typeof(Axis)))
            {
                <InputRadio Value="@entry" />
                <text>&nbsp;</text>@entry<br>
            }
        </InputRadioGroup>
    
        <button type="submit">Submit</button>
    </EditForm>
    
    <p>
        @message
    </p>
    
    @code {
        private RobotModel robotModel = new() { AxisSelection = Axis.Left };
        private string? message;
    
        private void HandleValidSubmit()
        {
            Logger.LogInformation("HandleValidSubmit called");
    
            var result = HandGesture.ThumbUp(robotModel.AxisSelection, Logger);
    
            message = $"ThumbUp returned {result} at {DateTime.Now}.";
        }
    
        public class RobotModel
        {
            public Axis AxisSelection { get; set; }
        }
    }
    

RCL のアセンブリの遅延読み込みの動作を示す Blazor WebAssembly アプリを作成します。

  1. Visual Studio、Visual Studio Code、または .NET CLI のコマンド プロンプトを使用して、Blazor WebAssembly アプリを作成します。 プロジェクトに LazyLoadTest という名前を付けます。

  2. GrantImaharaRobotControls RCL に対するプロジェクト参照を作成します。

    • Visual Studio: GrantImaharaRobotControls RCL プロジェクトをソリューションに追加します ( [追加]>[既存のプロジェクト] )。 [追加]>[プロジェクト参照] を選択して、GrantImaharaRobotControls RCL に対するプロジェクト参照を追加します。
    • Visual Studio Code/.NET CLI: プロジェクトのフォルダーからコマンド シェルで dotnet add reference {PATH} を実行します。 {PATH} プレースホルダーは、RCL プロジェクトへのパスです。

アプリケーションをビルドし、実行します。 Index コンポーネントを読み込む既定のページ (Pages/Index.razor) について、開発者ツールの [ネットワーク] タブに RCL のアセンブリ GrantImaharaRobotControls.dll が読み込まれていることが示されます。 Index コンポーネントにそのアセンブリは使用されないため、アセンブリを読み込むことは効率的ではありません。

インデックス コンポーネントがブラウザーに読み込まれ、開発者ツールの [ネットワーク] タブに、GrantImaharaRobotControls.dll アセンブリが読み込まれていることが示されます。

GrantImaharaRobotControls.dll アセンブリを遅延読み込みするようにアプリを構成します。

  1. Blazor WebAssembly アプリのプロジェクト ファイル (.csproj) で、遅延読み込みの対象である RCL のアセンブリを指定します。

    <ItemGroup>
      <BlazorWebAssemblyLazyLoad Include="GrantImaharaRobotControls.dll" />
    </ItemGroup>
    
  2. 次の Router コンポーネントによって、ユーザーが /robot に移動したときに GrantImaharaRobotControls.dll アセンブリを読み込む方法が実演されています。 アプリの既定の App コンポーネントを次の App コンポーネントに置き換えます。

    ページの切り替え中に、スタイル付きのメッセージが <Navigating> 要素を使用してユーザーに表示されます。 詳細については、「<Navigating> コンテンツとのユーザー操作」のセクションを参照してください。

    アセンブリは AdditionalAssemblies に割り当てられます。その結果、ルーターによってアセンブリでルーティング可能なコンポーネントが検索され、そこで Robot コンポーネントが見つかります。 Robot コンポーネントのルートがアプリのルート コレクションに追加されます。 詳しくは、記事「ASP.NET Core の Blazor ルーティングとナビゲーション」と、この記事の「ルーティング可能なコンポーネントを含むアセンブリ」セクションをご覧ください。

    App.razor:

    @using System.Reflection
    @using Microsoft.AspNetCore.Components.Routing
    @using Microsoft.AspNetCore.Components.WebAssembly.Services
    @using Microsoft.Extensions.Logging
    @inject LazyAssemblyLoader AssemblyLoader
    @inject ILogger<App> Logger
    
    <Router AppAssembly="@typeof(App).Assembly"
            AdditionalAssemblies="@lazyLoadedAssemblies" 
            OnNavigateAsync="@OnNavigateAsync">
        <Navigating>
            <div style="padding:20px;background-color:blue;color:white">
                <p>Loading the requested page&hellip;</p>
            </div>
        </Navigating>
        <Found Context="routeData">
            <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
        </Found>
        <NotFound>
            <LayoutView Layout="@typeof(MainLayout)">
                <p>Sorry, there's nothing at this address.</p>
            </LayoutView>
        </NotFound>
    </Router>
    
    @code {
        private List<Assembly> lazyLoadedAssemblies = new();
    
        private async Task OnNavigateAsync(NavigationContext args)
        {
            try
            {
                if (args.Path == "robot")
                {
                    var assemblies = await AssemblyLoader.LoadAssembliesAsync(
                        new[] { "GrantImaharaRobotControls.dll" });
                    lazyLoadedAssemblies.AddRange(assemblies);
                }
            }
            catch (Exception ex)
            {
                Logger.LogError("Error: {Message}", ex.Message);
            }
        }
    }
    

もう一度アプリをビルドし、実行します。 Index コンポーネントを読み込む既定のページ (Pages/Index.razor) について、開発者ツールの [ネットワーク] タブに、RCL のアセンブリ (GrantImaharaRobotControls.dll) が Index コンポーネント用に読み込まれて "いない" ことが示されます。

開発者ツールの [ネットワーク] タブに GrantImaharaRobotControls.dll が読み込まれなかったことが示された、ブラウザーに読み込まれたインデックス コンポーネント。

RCL の Robot コンポーネントが /robot で要求されると、GrantImaharaRobotControls.dll アセンブリが読み込まれ、Robot コンポーネントがレンダリングされます。

開発者ツールの [ネットワーク] タブに GrantImaharaRobotControls.dll アセンブリが読み込まれたことが示された、ブラウザーに読み込まれたロボット コンポーネント。

トラブルシューティング

  • レンダリングが予期しないものになった (たとえば、前のナビゲーションのコンポーネントがレンダリングされた) 場合は、キャンセル トークンが設定されている場合にコードがスローされることをご確認ください。
  • 遅延読み込み用に構成したアセンブリがアプリの開始時に予期せず読み込まれた場合は、プロジェクト ファイルでアセンブリが遅延読み込み対象としてマークされていることを確認します。

その他のリソース

Blazor WebAssembly アプリケーションの起動パフォーマンスは、アセンブリが必要になるまでアプリケーション アセンブリの読み込みを待機することで改善できます。これは "遅延読み込み" と呼ばれます。

この記事の初めのセクションでは、アプリの構成について説明します。 実際の動作のデモンストレーションについては、この記事の最後にある「コード例全体」のセクションを参照してください。

Note

Blazor Server アプリでは、アセンブリの遅延読み込みを行っても効果がありません。これは、Blazor Server アプリのアセンブリがクライアントにダウンロードされないためです。

プロジェクト ファイルの構成

アプリのプロジェクト ファイル (.csproj) 内で、BlazorWebAssemblyLazyLoad 項目を使用して、遅延読み込みのマークをアセンブリに付けます。 .dll 拡張子が含まれるアセンブリ名を使用します。 Blazor フレームワークを使用すると、アプリの起動時にアセンブリは読み込まれません。

<ItemGroup>
  <BlazorWebAssemblyLazyLoad Include="{ASSEMBLY NAME}.dll" />
</ItemGroup>

{ASSEMBLY NAME} プレースホルダーは、アセンブリの名前です。 .dll ファイル拡張子が必要です。

アセンブリごとに 1 個の BlazorWebAssemblyLazyLoad 項目を含めます。 アセンブリに依存関係がある場合は、依存関係ごとに BlazorWebAssemblyLazyLoad エントリを含めます。

Router コンポーネントの構成

Blazor フレームワークを使用すると、クライアント側の Blazor WebAssembly アプリ† である LazyAssemblyLoader で、アセンブリの遅延読み込みを行うシングルトン サービスが自動的に登録されます。 LazyAssemblyLoader.LoadAssembliesAsync メソッド:

  • JS 相互運用を使用して、ネットワーク呼び出しを介してアセンブリをフェッチします。
  • ブラウザー内の WebAssembly で実行されているランタイムにアセンブリを読み込みます。

†"ホストされた" Blazor WebAssemblyソリューションに関するガイダンスについては、「ホストされた Blazor WebAssembly ソリューションでのアセンブリの遅延読み込み」のセクションを参照してください。

Blazor の Router は、Blazor がルーティング可能なコンポーネントを求めて探索するアセンブリを指定し、ユーザーが移動するルートのコンポーネントをレンダリングする役割も担うコンポーネントです。 Router コンポーネントの OnNavigateAsync メソッドは、ユーザーが要求するエンドポイント用の正しいアセンブリを読み込むために、遅延読み込みと組み合わせて使用されます。

LazyAssemblyLoader を使用して読み込むアセンブリを決定するロジックは、OnNavigateAsync 内に実装されています。 ロジックを構成する方法のオプションは次のとおりです。

  • OnNavigateAsync メソッド内での条件チェック。
  • ルートをアセンブリ名にマップするルックアップ テーブル (コンポーネントに挿入されるか、@code ブロック内に実装される)。

次に例を示します。

  • Microsoft.AspNetCore.Components.WebAssembly.Services の名前空間が指定されています。
  • LazyAssemblyLoader サービスが挿入されます (AssemblyLoader)。
  • {PATH} プレースホルダーは、アセンブリのリストを読み込む場所のパスです。 この例では、1 つのアセンブリ セットを読み込む 1 つのパスの条件付きチェックを使用しています。
  • {LIST OF ASSEMBLIES} プレースホルダーは、.dll 拡張子 (たとえば、"Assembly1.dll", "Assembly2.dll") を含めたアセンブリのファイル名文字列のコンマ区切りのリストです。

App.razor:

@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.WebAssembly.Services
@using Microsoft.Extensions.Logging
@inject LazyAssemblyLoader AssemblyLoader
@inject ILogger<App> Logger

<Router AppAssembly="@typeof(Program).Assembly" 
    OnNavigateAsync="@OnNavigateAsync">
    ...
</Router>

@code {
    private async Task OnNavigateAsync(NavigationContext args)
    {
        try
           {
               if (args.Path == "{PATH}")
               {
                   var assemblies = await AssemblyLoader.LoadAssembliesAsync(
                       new[] { {LIST OF ASSEMBLIES} });
               }
           }
           catch (Exception ex)
           {
               Logger.LogError("Error: {Message}", ex.Message);
           }
    }
}

注意

前の例には、Router コンポーネントの Razor マークアップ (...) の内容が示されていません。 完全なコードのデモンストレーションについては、この記事の「コード例全体」のセクションを参照してください。

Note

ASP.NET Core 5.0.1 のリリースと、その他の 5.x リリースでは、Router コンポーネントに @trueに設定された PreferExactMatches パラメーターが含まれています。 詳細については、「ASP.NET Core 3.1 から 5.0 への移行」を参照してください。

ルーティング可能なコンポーネントを含むアセンブリ

アセンブリの一覧にルーティング可能なコンポーネントが含まれている場合は、指定されたパスのアセンブリ リストが Router コンポーネントの AdditionalAssemblies コレクションに渡されます。

次に例を示します。

  • lazyLoadedAssembliesList<Assembly> で AdditionalAssemblies にアセンブリ リストが渡されます。 フレームワークにおいてルートがアセンブリ内で検索され、新しいルートが見つかった場合はルート コレクションが更新されます。 Assembly 型にアクセスするために、System.Reflection の名前空間が App.razor ファイルの先頭に含まれています。
  • {PATH} プレースホルダーは、アセンブリのリストを読み込む場所のパスです。 この例では、1 つのアセンブリ セットを読み込む 1 つのパスの条件付きチェックを使用しています。
  • {LIST OF ASSEMBLIES} プレースホルダーは、.dll 拡張子 (たとえば、"Assembly1.dll", "Assembly2.dll") を含めたアセンブリのファイル名文字列のコンマ区切りのリストです。

App.razor:

@using System.Reflection
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.WebAssembly.Services
@using Microsoft.Extensions.Logging
@inject LazyAssemblyLoader AssemblyLoader
@inject ILogger<App> Logger

<Router AppAssembly="@typeof(Program).Assembly" 
    AdditionalAssemblies="@lazyLoadedAssemblies" 
    OnNavigateAsync="@OnNavigateAsync">
    ...
</Router>

@code {
    private List<Assembly> lazyLoadedAssemblies = new();

    private async Task OnNavigateAsync(NavigationContext args)
    {
        try
           {
               if (args.Path == "{PATH}")
               {
                   var assemblies = await AssemblyLoader.LoadAssembliesAsync(
                       new[] { {LIST OF ASSEMBLIES} });
                   lazyLoadedAssemblies.AddRange(assemblies);
               }
           }
           catch (Exception ex)
           {
               Logger.LogError("Error: {Message}", ex.Message);
           }
    }
}

注意

前の例には、Router コンポーネントの Razor マークアップ (...) の内容が示されていません。 完全なコードのデモンストレーションについては、この記事の「コード例全体」のセクションを参照してください。

Note

ASP.NET Core 5.0.1 のリリースと、その他の 5.x リリースでは、Router コンポーネントに @trueに設定された PreferExactMatches パラメーターが含まれています。 詳細については、「ASP.NET Core 3.1 から 5.0 への移行」を参照してください。

詳細については、「ASP.NET Core の Blazor ルーティングとナビゲーション」を参照してください。

<Navigating> コンテンツとのユーザー操作

アセンブリの読み込み中 (数秒かかることがある)、Router コンポーネントで Navigating プロパティを使用して、ページの切り替えが行われていることをユーザーに示すことができます。

詳細については、「ASP.NET Core の Blazor ルーティングとナビゲーション」を参照してください。

OnNavigateAsync でキャンセルを処理する

OnNavigateAsync コールバックに渡される NavigationContext オブジェクトには、新しいナビゲーション イベントが発生したときに設定される CancellationToken が含まれています。 そのキャンセル トークンが、古いナビゲーションに対して OnNavigateAsync コールバックを継続して実行しないように設定されている場合は、OnNavigateAsync コールバックをスローする必要があります。

詳細については、「ASP.NET Core の Blazor ルーティングとナビゲーション」を参照してください。

OnNavigateAsync イベントと名前が変更されたアセンブリ ファイル

リソース ローダーでは、blazor.boot.json ファイルで定義されているアセンブリ名が使用されます。 アセンブリの名前が変更された場合、OnNavigateAsync コールバックで使用されるアセンブリ名と blazor.boot.json ファイル内のアセンブリ名が同期されなくなります。

これを修正するには:

  • 使用するアセンブリ名を決定するときに、アプリが Production 環境で実行されているかどうかを確認します。
  • 名前を変更したアセンブリ名を別のファイルに格納し、そのファイルから読み取りを行い、LazyAssemblyLoader サービスおよび OnNavigateAsync コールバックで使用するアセンブリ名を決定します。

ホストされた Blazor WebAssembly ソリューションでのアセンブリの遅延読み込み

フレームワークの遅延読み込みの実装では、ホストされた Blazor WebAssemblyソリューションでのプリレンダリングによる遅延読み込みがサポートされます。 プリレンダリング中は、遅延読み込みのマークが付けられたものも含め、すべてのアセンブリが読み込まれると見なされます。 Server プロジェクトに LazyAssemblyLoader サービスを手動で登録します。

Server プロジェクトの Startup.cs ファイルの先頭に、Microsoft.AspNetCore.Components.WebAssembly.Services の名前空間を追加します。

using Microsoft.AspNetCore.Components.WebAssembly.Services;

Server プロジェクトの Startup.ConfigureServices メソッド (Startup.cs) で、サービスを登録します。

services.AddScoped<LazyAssemblyLoader>();

コード例全体

このセクションのデモンストレーションの内容は次のとおりです。

  • Robot コンポーネント (/robot のルート テンプレートを使用した Robot.razor) を含む Razor クラス ライブラリ (RCL) としてロボット コントロール アセンブリ (GrantImaharaRobotControls.dll) を作成します。
  • ユーザーから Robot の URL が要求されたときに /robot コンポーネントをレンダリングするために、RCL のアセンブリを遅延読み込みします。
  1. 新しい ASP.NET Core クラス ライブラリ プロジェクトを作成します。

    • Visual Studio: [ソリューションの作成]>[新しいプロジェクトの作成]>Razor [クラス ライブラリ] 。 プロジェクトに GrantImaharaRobotControls という名前を付けます。
    • Visual Studio Code/.NET CLI: コマンド プロンプトから dotnet new razorclasslib -o GrantImaharaRobotControls を実行します。 -o|--output オプションでソリューションのフォルダーを作成し、プロジェクトに GrantImaharaRobotControls という名前を付けます。
  2. このセクションの後半に示すコンポーネント例では、Blazor フォームを使用します。 RCL プロジェクトに、Microsoft.AspNetCore.Components.Forms パッケージを追加します。

    Note

    .NET アプリへのパッケージの追加に関するガイダンスについては、「パッケージ利用のワークフロー」 (NuGet ドキュメント) の "パッケージのインストールと管理" に関する記事を参照してください。 NuGet.org で正しいパッケージ バージョンを確認します。

  3. ロボットに親指を立てるジェスチャを実行させると仮定した ThumbUp メソッドを使用して、RCL に HandGesture クラスを作成します。 そのメソッドに軸の引数 (Left または Right) を enum として渡します。 メソッドが正常に終了すると、true が返されます。

    HandGesture.cs:

    using Microsoft.Extensions.Logging;
    
    namespace GrantImaharaRobotControls
    {
        public static class HandGesture
        {
            public static bool ThumbUp(Axis axis, ILogger logger)
            {
                logger.LogInformation("Thumb up gesture. Axis: {Axis}", axis);
    
                // Code to make robot perform gesture
    
                return true;
            }
        }
    
        public enum Axis { Left, Right }
    }
    
  4. 次のコンポーネントを RCL プロジェクトのルートに追加します。 このコンポーネントでユーザーは左手または右手の親指を立てるジェスチャ要求を送信できます。

    Robot.razor:

    @page "/robot"
    @using Microsoft.AspNetCore.Components.Forms
    @using Microsoft.Extensions.Logging
    @inject ILogger<Robot> Logger
    
    <h1>Robot</h1>
    
    <EditForm Model="@robotModel" OnValidSubmit="@HandleValidSubmit">
        <InputRadioGroup @bind-Value="robotModel.AxisSelection">
            @foreach (var entry in (Axis[])Enum
                .GetValues(typeof(Axis)))
            {
                <InputRadio Value="@entry" />
                <text>&nbsp;</text>@entry<br>
            }
        </InputRadioGroup>
    
        <button type="submit">Submit</button>
    </EditForm>
    
    <p>
        @message
    </p>
    
    @code {
        private RobotModel robotModel = new() { AxisSelection = Axis.Left };
        private string message;
    
        private void HandleValidSubmit()
        {
            Logger.LogInformation("HandleValidSubmit called");
    
            var result = HandGesture.ThumbUp(robotModel.AxisSelection, Logger);
    
            message = $"ThumbUp returned {result} at {DateTime.Now}.";
        }
    
        public class RobotModel
        {
            public Axis AxisSelection { get; set; }
        }
    }
    

RCL のアセンブリの遅延読み込みの動作を示す Blazor WebAssembly アプリを作成します。

  1. Visual Studio、Visual Studio Code、または .NET CLI のコマンド プロンプトを使用して、Blazor WebAssembly アプリを作成します。 プロジェクトに LazyLoadTest という名前を付けます。

  2. GrantImaharaRobotControls RCL に対するプロジェクト参照を作成します。

    • Visual Studio: GrantImaharaRobotControls RCL プロジェクトをソリューションに追加します ([追加]>[既存のプロジェクト])。 [追加]>[プロジェクト参照] を選択して、GrantImaharaRobotControls RCL に対するプロジェクト参照を追加します。
    • Visual Studio Code/.NET CLI: プロジェクトのフォルダーからコマンド シェルで dotnet add reference {PATH} を実行します。 {PATH} プレースホルダーは、RCL プロジェクトへのパスです。

アプリケーションをビルドし、実行します。 Index コンポーネントを読み込む既定のページ (Pages/Index.razor) について、開発者ツールの [ネットワーク] タブに RCL のアセンブリ GrantImaharaRobotControls.dll が読み込まれていることが示されます。 Index コンポーネントにそのアセンブリは使用されないため、アセンブリを読み込むことは効率的ではありません。

インデックス コンポーネントがブラウザーに読み込まれ、開発者ツールの [ネットワーク] タブに、GrantImaharaRobotControls.dll アセンブリが読み込まれていることが示されます。

GrantImaharaRobotControls.dll アセンブリを遅延読み込みするようにアプリを構成します。

  1. Blazor WebAssembly アプリのプロジェクト ファイル (.csproj) で、遅延読み込みの対象である RCL のアセンブリを指定します。

    <ItemGroup>
      <BlazorWebAssemblyLazyLoad Include="GrantImaharaRobotControls.dll" />
    </ItemGroup>
    
  2. 次の Router コンポーネントによって、ユーザーが /robot に移動したときに GrantImaharaRobotControls.dll アセンブリを読み込む方法が実演されています。 アプリの既定の App コンポーネントを次の App コンポーネントに置き換えます。

    ページの切り替え中に、スタイル付きのメッセージが <Navigating> 要素を使用してユーザーに表示されます。 詳細については、「<Navigating> コンテンツとのユーザー操作」のセクションを参照してください。

    アセンブリは AdditionalAssemblies に割り当てられます。その結果、ルーターによってアセンブリでルーティング可能なコンポーネントが検索され、そこで Robot コンポーネントが見つかります。 Robot コンポーネントのルートがアプリのルート コレクションに追加されます。 詳しくは、記事「ASP.NET Core の Blazor ルーティングとナビゲーション」と、この記事の「ルーティング可能なコンポーネントを含むアセンブリ」セクションをご覧ください。

    App.razor:

    @using System.Reflection
    @using Microsoft.AspNetCore.Components.Routing
    @using Microsoft.AspNetCore.Components.WebAssembly.Services
    @using Microsoft.Extensions.Logging
    @inject LazyAssemblyLoader AssemblyLoader
    @inject ILogger<App> Logger
    
    <Router AppAssembly="@typeof(Program).Assembly"
            AdditionalAssemblies="@lazyLoadedAssemblies" 
            OnNavigateAsync="@OnNavigateAsync">
        <Navigating>
            <div style="padding:20px;background-color:blue;color:white">
                <p>Loading the requested page&hellip;</p>
            </div>
        </Navigating>
        <Found Context="routeData">
            <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
        </Found>
        <NotFound>
            <LayoutView Layout="@typeof(MainLayout)">
                <p>Sorry, there's nothing at this address.</p>
            </LayoutView>
        </NotFound>
    </Router>
    
    @code {
        private List<Assembly> lazyLoadedAssemblies = new();
    
        private async Task OnNavigateAsync(NavigationContext args)
        {
            try
            {
                if (args.Path == "robot")
                {
                    var assemblies = await AssemblyLoader.LoadAssembliesAsync(
                        new[] { "GrantImaharaRobotControls.dll" });
                    lazyLoadedAssemblies.AddRange(assemblies);
                }
            }
            catch (Exception ex)
            {
                Logger.LogError("Error: {Message}", ex.Message);
            }
        }
    }
    

    注意

    ASP.NET Core 5.0.1 のリリースと、その他の 5.x リリースでは、Router コンポーネントに @trueに設定された PreferExactMatches パラメーターが含まれています。 詳細については、「ASP.NET Core 3.1 から 5.0 への移行」を参照してください。

もう一度アプリをビルドし、実行します。 Index コンポーネントを読み込む既定のページ (Pages/Index.razor) について、開発者ツールの [ネットワーク] タブに、RCL のアセンブリ (GrantImaharaRobotControls.dll) が Index コンポーネント用に読み込まれて "いない" ことが示されます。

開発者ツールの [ネットワーク] タブに GrantImaharaRobotControls.dll が読み込まれなかったことが示された、ブラウザーに読み込まれたインデックス コンポーネント。

RCL の Robot コンポーネントが /robot で要求されると、GrantImaharaRobotControls.dll アセンブリが読み込まれ、Robot コンポーネントがレンダリングされます。

開発者ツールの [ネットワーク] タブに GrantImaharaRobotControls.dll アセンブリが読み込まれたことが示された、ブラウザーに読み込まれたロボット コンポーネント。

トラブルシューティング

  • レンダリングが予期しないものになった (たとえば、前のナビゲーションのコンポーネントがレンダリングされた) 場合は、キャンセル トークンが設定されている場合にコードがスローされることをご確認ください。
  • 遅延読み込み用に構成したアセンブリがアプリの開始時に予期せず読み込まれた場合は、プロジェクト ファイルでアセンブリが遅延読み込み対象としてマークされていることを確認します。

Note

遅延読み込みされたアセンブリから型を読み込む場合、既知の問題が存在します。 詳細については、「Blazor WebAssembly lazy loading assemblies not working when using @ref attribute in the component (dotnet/aspnetcore #29342)」を参照してください。

その他のリソース