次の方法で共有


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

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

警告

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

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

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

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

遅延読み込みはコアのランタイム アセンブリには使用しないでください。これは、発行時にトリミングされ、アプリの読み込み時にクライアントで使用できなくなるおそれがあります。

アセンブリ ファイルのファイル拡張子プレースホルダー ({FILE EXTENSION})

アセンブリ ファイルは、Webcil パッケージング形式を使用して .NET アセンブリを扱い、ファイル拡張子として.wasmを使用します。

この記事全体を通して、プレースホルダー {FILE EXTENSION} は "wasm" を表します。

アセンブリ ファイルは、ファイル拡張子 .dll を持つダイナミック リンク ライブラリ (DLL) に基づいています。

この記事全体を通して、プレースホルダー {FILE EXTENSION} は "dll" を表します。

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

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

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

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

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

Router コンポーネントの構成

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

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

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

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

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

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

次に例を示します。

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

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(
                       [ {LIST OF ASSEMBLIES} ]);
               }
           }
           catch (Exception ex)
           {
               Logger.LogError("Error: {Message}", ex.Message);
           }
    }
}
@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);
           }
    }
}
@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 マークアップ (...) の内容が示されていません。 完全なコードのデモンストレーションについては、この記事の「コード例全体」のセクションを参照してください。

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

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

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

次に例を示します。

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

App.razor:

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

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

@code {
    private List<Assembly> lazyLoadedAssemblies = [];

    private async Task OnNavigateAsync(NavigationContext args)
    {
        try
        {
            if (args.Path == "{PATH}")
            {
                var assemblies = await AssemblyLoader.LoadAssembliesAsync(
                    [ {LIST OF ASSEMBLIES} ]);
                lazyLoadedAssemblies.AddRange(assemblies);
            }
        }
        catch (Exception ex)
        {
            Logger.LogError("Error: {Message}", ex.Message);
        }
    }
}
@using System.Reflection
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.WebAssembly.Services
@using Microsoft.Extensions.Logging
@inject ILogger<App> Logger
@inject LazyAssemblyLoader AssemblyLoader

<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);
        }
    }
}
@using System.Reflection
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.WebAssembly.Services
@using Microsoft.Extensions.Logging
@inject ILogger<App> Logger
@inject LazyAssemblyLoader AssemblyLoader

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

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

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

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

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

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

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

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

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

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

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

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

リソース ローダーは、ブート マニフェスト ファイルで定義されているアセンブリ名に依存します。 アセンブリの名前が変更されるとOnNavigateAsync コールバックで使用されるアセンブリ名とブート マニフェスト ファイル内のアセンブリ名が同期されません。

これを修正するには:

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

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

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

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

using Microsoft.AspNetCore.Components.WebAssembly.Services;

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

builder.Services.AddScoped<LazyAssemblyLoader>();

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

using Microsoft.AspNetCore.Components.WebAssembly.Services;

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

services.AddScoped<LazyAssemblyLoader>();

完全な例

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

  • GrantImaharaRobotControls.{FILE EXTENSION} コンポーネント (ルート テンプレートを Razor とする ) を含む Robot としてロボット コントロール アセンブリ (Robot.razor) を作成します。
  • ユーザーから Robot の URL が要求されたときに /robot コンポーネントをレンダリングするために、RCL のアセンブリを遅延読み込みします。

スタンドアロン Blazor WebAssembly アプリを作成して、Razor クラスライブラリのアセンブリの遅延読み込みを示します。 プロジェクトに LazyLoadTest という名前を付けます。

ASP.NET Core クラス ライブラリ プロジェクトをソリューションに追加します。

  • Visual Studio: ソリューション エクスプローラーでソリューション ファイルを右クリックし、[追加]>[新しいプロジェクト] の順に選択します。 新しいプロジェクト タイプのダイアログから、Razor クラス ライブラリを選択します。 プロジェクトに GrantImaharaRobotControls という名前を付けます。 [サポート ページとビュー] チェック ボックスはオンにしないでください
  • Visual Studio Code/.NET CLI: コマンド プロンプトから dotnet new razorclasslib -o GrantImaharaRobotControls を実行します。 -o|--output オプションでフォルダーを作成し、プロジェクトに GrantImaharaRobotControls という名前を付けます。

ロボットに親指を立てるジェスチャを実行させると仮定した HandGesture メソッドを使用して、RCL に ThumbUp クラスを作成します。 そのメソッドに軸の引数 (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 }
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 }
}

次のコンポーネントを RCL プロジェクトのルートに追加します。 このコンポーネントでユーザーは左手または右手の親指を立てるジェスチャ要求を送信できます。

Robot.razor:

@page "/robot"
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.Extensions.Logging
@inject ILogger<Robot> Logger

<h1>Robot</h1>

<EditForm FormName="RobotForm" Model="robotModel" OnValidSubmit="HandleValidSubmit">
    <InputRadioGroup @bind-Value="robotModel.AxisSelection">
        @foreach (var entry in Enum.GetValues<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; }
    }
}
@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 Enum.GetValues<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; }
    }
}
@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 RobotModel() { 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; }
    }
}

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

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

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

<ItemGroup>
    <BlazorWebAssemblyLazyLoad Include="GrantImaharaRobotControls.{FILE EXTENSION}" />
</ItemGroup>

次の Router コンポーネントによって、ユーザーが GrantImaharaRobotControls.{FILE EXTENSION} に移動したときに /robot アセンブリを読み込む方法が実演されています。 アプリの既定の 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 ILogger<App> Logger
@inject LazyAssemblyLoader AssemblyLoader

<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 = [];
    private bool grantImaharaRobotControlsAssemblyLoaded;

    private async Task OnNavigateAsync(NavigationContext args)
    {
        try
        {
            if ((args.Path == "robot") && !grantImaharaRobotControlsAssemblyLoaded)
            {
                var assemblies = await AssemblyLoader.LoadAssembliesAsync(
                    [ "GrantImaharaRobotControls.{FILE EXTENSION}" ]);
                lazyLoadedAssemblies.AddRange(assemblies);
                grantImaharaRobotControlsAssemblyLoaded = true;
            }
        }
        catch (Exception ex)
        {
            Logger.LogError("Error: {Message}", ex.Message);
        }
    }
}
@using System.Reflection
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.WebAssembly.Services
@using Microsoft.Extensions.Logging
@inject ILogger<App> Logger
@inject LazyAssemblyLoader AssemblyLoader

<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 bool grantImaharaRobotControlsAssemblyLoaded;

    private async Task OnNavigateAsync(NavigationContext args)
    {
        try
        {
            if ((args.Path == "robot") && !grantImaharaRobotControlsAssemblyLoaded)
            {
                var assemblies = await AssemblyLoader.LoadAssembliesAsync(
                    new[] { "GrantImaharaRobotControls.{FILE EXTENSION}" });
                lazyLoadedAssemblies.AddRange(assemblies);
                grantImaharaRobotControlsAssemblyLoaded = true;
            }
        }
        catch (Exception ex)
        {
            Logger.LogError("Error: {Message}", ex.Message);
        }
    }
}
@using System.Reflection
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.WebAssembly.Services
@using Microsoft.Extensions.Logging
@inject ILogger<App> Logger
@inject LazyAssemblyLoader AssemblyLoader

<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 List<Assembly>();
    private bool grantImaharaRobotControlsAssemblyLoaded;

    private async Task OnNavigateAsync(NavigationContext args)
    {
        try
        {
            if ((args.Path == "robot") && !grantImaharaRobotControlsAssemblyLoaded)
            {
                var assemblies = await AssemblyLoader.LoadAssembliesAsync(
                    new[] { "GrantImaharaRobotControls.{FILE EXTENSION}" });
                lazyLoadedAssemblies.AddRange(assemblies);
                grantImaharaRobotControlsAssemblyLoaded = true;
            }
        }
        catch (Exception ex)
        {
            Logger.LogError("Error: {Message}", ex.Message);
        }
    }
}

アプリケーションをビルドし、実行します。

RCL の Robot コンポーネントが /robot で要求されると、GrantImaharaRobotControls.{FILE EXTENSION} アセンブリが読み込まれ、Robot コンポーネントがレンダリングされます。 アセンブリの読み込みは、ブラウザーの開発者ツールの [ネットワーク] タブで確認できます。

トラブルシューティング

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

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

その他のリソース