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

注意

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

重要

この情報はリリース前の製品に関する事項であり、正式版がリリースされるまでに大幅に変更される可能性があります。 Microsoft はここに示されている情報について、明示か黙示かを問わず、一切保証しません。

現在のリリースについては、この記事の .NET 8 バージョンを参照してください。

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

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

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

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

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

アセンブリ ファイルには、ファイル拡張子 .wasm を持つ .NET アセンブリの Webcil パッケージ形式を使用します。

この記事全体を通して、プレースホルダー {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 で実行されているランタイムにアセンブリを読み込みます。

Note

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

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

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 プロジェクトの Program.cs ファイルの先頭に、Microsoft.AspNetCore.Components.WebAssembly.Services の名前空間を追加します。

using Microsoft.AspNetCore.Components.WebAssembly.Services;

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

builder.Services.AddScoped<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.{FILE EXTENSION}) を作成します。
  • ユーザーから Robot の URL が要求されたときに /robot コンポーネントをレンダリングするために、RCL のアセンブリを遅延読み込みします。

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

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

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

このセクションの後半に示すコンポーネント例では、Blazor フォームを使用します。 RCL プロジェクトに、Microsoft.AspNetCore.Components.Forms パッケージを追加します。

Note

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

ロボットに親指を立てるジェスチャを実行させると仮定した 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 }
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 コンポーネントによって、ユーザーが /robot に移動したときに GrantImaharaRobotControls.{FILE EXTENSION} アセンブリを読み込む方法が実演されています。 アプリの既定の 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 = new();

    private async Task OnNavigateAsync(NavigationContext args)
    {
        try
        {
            if (args.Path == "robot")
            {
                var assemblies = await AssemblyLoader.LoadAssembliesAsync(
                    new[] { "GrantImaharaRobotControls.{FILE EXTENSION}" });
                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">
    <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 async Task OnNavigateAsync(NavigationContext args)
    {
        try
        {
            if (args.Path == "robot")
            {
                var assemblies = await AssemblyLoader.LoadAssembliesAsync(
                    new[] { "GrantImaharaRobotControls.{FILE EXTENSION}" });
                lazyLoadedAssemblies.AddRange(assemblies);
            }
        }
        catch (Exception ex)
        {
            Logger.LogError("Error: {Message}", ex.Message);
        }
    }
}

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

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

トラブルシューティング

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

Note

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

その他のリソース