ASP.NET Core Razor コンポーネントのライフサイクルASP.NET Core Razor component lifecycle

Razor コンポーネントは、一連の同期および非同期のライフサイクル メソッド内の Razor コンポーネント ライフサイクル イベントを処理します。The Razor component processes Razor component lifecycle events in a set of synchronous and asynchronous lifecycle methods. ライフサイクル メソッドをオーバーライドして、コンポーネントの初期化およびレンダリング中にコンポーネントで追加の操作を実行できます。The lifecycle methods can be overridden to perform additional operations in components during component initialization and rendering.

ライフサイクル イベントLifecycle events

次の図は、 Razor コンポーネント ライフサイクル イベントを示しています。The following diagrams illustrate Razor component lifecycle events. ライフサイクル イベントに関連付けられている C# メソッドは、この記事の次のセクションで例と共に定義されています。The C# methods associated with the lifecycle events are defined with examples in the following sections of this article.

コンポーネント ライフサイクル イベント:Component lifecycle events:

  1. 要求に対してコンポーネントが初めてレンダリングされる場合は、次のようにします。If the component is rendering for the first time on a request:
    • コンポーネントのインスタンスを作成します。Create the component's instance.
    • プロパティの挿入を実行します。Perform property injection. SetParametersAsync を実行します。Run SetParametersAsync.
    • OnInitialized{Async} を呼び出します。Call OnInitialized{Async}. 未完了の Task が返された場合は、Task を待機してから、コンポーネントが再レンダリングされます。If an incomplete Task is returned, the Task is awaited and then the component is rerendered.
  2. OnParametersSet{Async} を呼び出します。Call OnParametersSet{Async}. 未完了の Task が返された場合は、Task を待機してから、コンポーネントが再レンダリングされます。If an incomplete Task is returned, the Task is awaited and then the component is rerendered.
  3. すべての同期作業に対してレンダリングし、Task を完了します。Render for all synchronous work and complete Tasks.

Blazor の Razor コンポーネントのコンポーネント ライフサイクル イベント

ドキュメント オブジェクト モデル (DOM) イベント処理:Document Object Model (DOM) event processing:

  1. イベント ハンドラーが実行されます。The event handler is run.
  2. 未完了の Task が返された場合は、Task を待機してから、コンポーネントが再レンダリングされます。If an incomplete Task is returned, the Task is awaited and then the component is rerendered.
  3. すべての同期作業に対してレンダリングし、Task を完了します。Render for all synchronous work and complete Tasks.

ドキュメント オブジェクト モデル (DOM) イベント処理

Render のライフサイクル:The Render lifecycle:

  1. コンポーネントでそれ以上のレンダリング操作を行わないようにします。Avoid further rendering operations on the component:
    • 最初のレンダリングの後。After the first render.
    • ShouldRenderfalse の場合。When ShouldRender is false.
  2. レンダリング ツリーの差分を作成し、コンポーネントをレンダリングします。Build the render tree diff (difference) and render the component.
  3. DOM が更新されるのを待機します。Await the DOM to update.
  4. OnAfterRender{Async} を呼び出します。Call OnAfterRender{Async}.

Render ライフサイクル

Developer によって StateHasChanged の呼び出しが行われると、結果としてレンダリングが実行されます。Developer calls to StateHasChanged result in a render. 詳細については、「ASP.NET Core Blazor コンポーネントのレンダリング」を参照してください。For more information, see ASP.NET Core Blazor コンポーネントのレンダリング.

パラメーターが設定されるタイミング (SetParametersAsync)When parameters are set (SetParametersAsync)

SetParametersAsync により、レンダリング ツリーのコンポーネントの親によって、またはルート パラメーターから指定されたパラメーターが設定されます。SetParametersAsync sets parameters supplied by the component's parent in the render tree or from route parameters.

メソッドの ParameterView パラメーターには、SetParametersAsync が呼び出されるたびに、コンポーネントに対するコンポーネント パラメーター値のセットが格納されます。The method's ParameterView parameter contains the set of component parameter values for the component each time SetParametersAsync is called. 開発者のコードでは、SetParametersAsync メソッドをオーバーライドすることによって、ParameterView のパラメーターを直接操作できます。By overriding the SetParametersAsync method, developer code can interact directly with ParameterView's parameters.

SetParametersAsync の既定の実装では、対応する値が ParameterView 内にある [Parameter] または [CascadingParameter] 属性を使用して、各プロパティの値が設定されます。The default implementation of SetParametersAsync sets the value of each property with the [Parameter] or [CascadingParameter] attribute that has a corresponding value in the ParameterView. 対応する値が ParameterView 内にないパラメーターは、変更されないままになります。Parameters that don't have a corresponding value in ParameterView are left unchanged.

base.SetParametersAsync が呼び出されない場合、開発者コードでは、必要に応じて受信パラメーター値を解釈できます。If base.SetParametersAsync isn't invoked, developer code can interpret the incoming parameters' values in any way required. たとえば、受信したパラメーターをクラスのプロパティに割り当てる必要はありません。For example, there's no requirement to assign the incoming parameters to the properties of the class.

開発者コード内にイベント ハンドラーが提供されている場合、破棄時にこれらをアンフックします。If event handlers are provided in developer code, unhook them on disposal. 詳細については、「IDisposable を使用したコンポーネントの破棄」セクションを参照してください。For more information, see the Component disposal with IDisposable section.

次の例では、Param のルート パラメーターの解析が成功した場合、ParameterView.TryGetValue によって Param パラメーターの値が value に代入されます。In the following example, ParameterView.TryGetValue assigns the Param parameter's value to value if parsing a route parameter for Param is successful. valuenull でなければ、コンポーネントによってその値が表示されます。When value isn't null, the value is displayed by the component.

ルート パラメーターの照合では大文字と小文字が区別されませんが、ルート テンプレートでは TryGetValue によってパラメーター名が大文字と小文字を区別して照合されます。Although route parameter matching is case insensitive, TryGetValue only matches case sensitive parameter names in the route template. 次の例では、TryGetValue で値を取得するために、ルート テンプレートで /{param?} ではなく /{Param?} を使用する必要があります。The following example requires the use of /{Param?} in the route template in order to get the value with TryGetValue, not /{param?}. このシナリオで /{param?} が使用される場合、TryGetValue から false が返され、message はどちらの message 文字列にも設定されません。If /{param?} is used in this scenario, TryGetValue returns false and message isn't set to either message string.

Pages/SetParamsAsync.razor:Pages/SetParamsAsync.razor:

@page "/set-params-async/{Param?}"

<p>@message</p>

@code {
    private string message = "Not set";

    [Parameter]
    public string Param { get; set; }

    public override async Task SetParametersAsync(ParameterView parameters)
    {
        if (parameters.TryGetValue<string>(nameof(Param), out var value))
        {
            if (value is null)
            {
                message = "The value of 'Param' is null.";
            }
            else
            {
                message = $"The value of 'Param' is {value}.";
            }
        }

        await base.SetParametersAsync(parameters);
    }
}
@page "/set-params-async"
@page "/set-params-async/{Param}"

<p>@message</p>

@code {
    private string message = "Not set";

    [Parameter]
    public string Param { get; set; }

    public override async Task SetParametersAsync(ParameterView parameters)
    {
        if (parameters.TryGetValue<string>(nameof(Param), out var value))
        {
            if (value is null)
            {
                message = "The value of 'Param' is null.";
            }
            else
            {
                message = $"The value of 'Param' is {value}.";
            }
        }

        await base.SetParametersAsync(parameters);
    }
}

コンポーネントの初期化 (OnInitialized{Async})Component initialization (OnInitialized{Async})

OnInitialized および OnInitializedAsync は、コンポーネントが SetParametersAsync で初期パラメーターを受け取った後で初期化されるときに呼び出されます。OnInitialized and OnInitializedAsync are invoked when the component is initialized after having received its initial parameters in SetParametersAsync.

同期操作の場合は、OnInitialized をオーバーライドします。For a synchronous operation, override OnInitialized:

Pages/OnInit.razor:Pages/OnInit.razor:

@page "/on-init"

<p>@message</p>

@code {
    private string message;

    protected override void OnInitialized()
    {
        message = $"Initialized at {DateTime.Now}";
    }
}
@page "/on-init"

<p>@message</p>

@code {
    private string message;

    protected override void OnInitialized()
    {
        message = $"Initialized at {DateTime.Now}";
    }
}

非同期操作を実行するには、OnInitializedAsync をオーバーライドし、await 演算子を使用します。To perform an asynchronous operation, override OnInitializedAsync and use the await operator:

protected override async Task OnInitializedAsync()
{
    await ...
}

コンテンツをプリレンダリングする Blazor アプリによって、OnInitializedAsync が "2 回" 呼び出されます。Blazor apps that prerender their content call OnInitializedAsync twice:

  • コンポーネントが最初にページの一部として静的にレンダリングされるときに 1 回。Once when the component is initially rendered statically as part of the page.
  • 2 回目はブラウザーがコンポーネントをレンダリングするとき。A second time when the browser renders the component.

Blazor Server アプリでプリレンダリングするとき、OnInitializedAsync 内で開発者コードが 2 回実行されないようにするには、「プリレンダリング後のステートフル再接続」セクションを参照してください。To prevent developer code in OnInitializedAsync from running twice when prerendering in Blazor Server apps, see the Stateful reconnection after prerendering section. このセクションのコンテンツでは、Blazor Server およびステートフルな SignalR の "再接続" に焦点を当てていますが、ホストされた Blazor WebAssembly アプリ (WebAssemblyPrerendered) でのプリレンダリングのシナリオでは、開発者コードを 2 回実行しないようにするための同様の条件とアプローチが必要です。Although the content in the section focuses on Blazor Server and stateful SignalR reconnection, the scenario for prerendering in hosted Blazor WebAssembly apps (WebAssemblyPrerendered) involves similar conditions and approaches to prevent executing developer code twice. ASP.NET Core 6.0 リリースでは、プリレンダリング中の初期化コードの実行の管理を向上させる "新しい状態保存機能" が計画されています。A new state preservation feature is planned for the ASP.NET Core 6.0 release that will improve the management of initialization code execution during prerendering.

Blazor アプリがプリレンダリングされている間、JavaScript の呼び出し (JS 相互運用) などの特定のアクションは不可能です。While a Blazor app is prerendering, certain actions, such as calling into JavaScript (JS interop), aren't possible. コンポーネントは、プリレンダリング時に異なるレンダリングが必要になる場合があります。Components may need to render differently when prerendered. 詳細については、「アプリがプリレンダリングされていることを検出する」セクションを参照してください。For more information, see the Detect when the app is prerendering section.

開発者コード内にイベント ハンドラーが提供されている場合、破棄時にこれらをアンフックします。If event handlers are provided in developer code, unhook them on disposal. 詳細については、「IDisposable を使用したコンポーネントの破棄」セクションを参照してください。For more information, see the Component disposal with IDisposable section.

パラメーターが設定された後 (OnParametersSet{Async})After parameters are set (OnParametersSet{Async})

OnParametersSet または OnParametersSetAsync が呼び出されます。OnParametersSet or OnParametersSetAsync are called:

  • コンポーネントが OnInitialized または OnInitializedAsync で初期化された後。After the component is initialized in OnInitialized or OnInitializedAsync.
  • 親コンポーネントが再レンダリングし、次のものを提供するとき:When the parent component rerenders and supplies:
    • 既知のプリミティブ不変型 (少なくとも 1 つのパラメーターが変更された場合)。Known primitive immutable types when at least one parameter has changed.
    • 複合型のパラメーター。Complex-typed parameters. フレームワークは、複合型のパラメーターの値が内部で変更されているかどうかを認識できないため、1 つ以上の複合型のパラメーターが存在する場合、フレームワークではパラメーター セットが常に変更済みとして扱われます。The framework can't know whether the values of a complex-typed parameter have mutated internally, so the framework always treats the parameter set as changed when one or more complex-typed parameters are present.

次のコンポーネントの例では、URL でコンポーネントのページに移動します。For the following example component, navigate to the component's page at a URL:

  • StartDate によって受信された開始日がある場合: /on-parameters-set/2021-03-19With a start date that's received by StartDate: /on-parameters-set/2021-03-19
  • 開始日がなく、StartDate に現在の現地時刻の値が割り当てられている場合: /on-parameters-setWithout a start date, where StartDate is assigned a value of the current local time: /on-parameters-set

Pages/OnParamsSet.razor:Pages/OnParamsSet.razor:

注意

コンポーネント ルートでは、DateTime パラメーターをルート制約 datetime で制限すること、およびパラメーターを省略可能にすることを両方行うことはできません。In a component route, it isn't possible to both constrain a DateTime parameter with the route constraint datetime and make the parameter optional. したがって、次の OnParamsSet コンポーネントでは、2 つの @page ディレクティブを使用して、URL に指定された日付セグメントを含む場合と含まない場合のルーティングを処理しています。Therefore, the following OnParamsSet component uses two @page directives to handle routing with and without a supplied date segment in the URL.

@page "/on-params-set"
@page "/on-params-set/{StartDate:datetime}"

<p>@message</p>

@code {
    private string message;

    [Parameter]
    public DateTime StartDate { get; set; }

    protected override void OnParametersSet()
    {
        if (StartDate == default)
        {
            StartDate = DateTime.Now;

            message = $"No start date in URL. Default value applied (StartDate: {StartDate}).";
        }
        else
        {
            message = $"The start date in the URL was used (StartDate: {StartDate}).";
        }
    }
}
@page "/on-params-set"
@page "/on-params-set/{StartDate:datetime}"

<p>@message</p>

@code {
    private string message;

    [Parameter]
    public DateTime StartDate { get; set; }

    protected override void OnParametersSet()
    {
        if (StartDate == default)
        {
            StartDate = DateTime.Now;

            message = $"No start date in URL. Default value applied (StartDate: {StartDate}).";
        }
        else
        {
            message = $"The start date in the URL was used (StartDate: {StartDate}).";
        }
    }
}

パラメーターとプロパティ値を適用するときの非同期処理は、OnParametersSetAsync ライフサイクル イベント中に発生する必要があります。Asynchronous work when applying parameters and property values must occur during the OnParametersSetAsync lifecycle event:

protected override async Task OnParametersSetAsync()
{
    await ...
}

開発者コード内にイベント ハンドラーが提供されている場合、破棄時にこれらをアンフックします。If event handlers are provided in developer code, unhook them on disposal. 詳細については、「IDisposable を使用したコンポーネントの破棄」セクションを参照してください。For more information, see the Component disposal with IDisposable section.

ルート パラメーターと制約の詳細については、「ASP.NET Core Blazor のルーティング」を参照してください。For more information on route parameters and constraints, see ASP.NET Core Blazor のルーティング.

コンポーネントのレンダリング後 (OnAfterRender{Async})After component render (OnAfterRender{Async})

OnAfterRender および OnAfterRenderAsync は、コンポーネントのレンダリングが完了した後に呼び出されます。OnAfterRender and OnAfterRenderAsync are called after a component has finished rendering. この時点で、要素およびコンポーネント参照が設定されます。Element and component references are populated at this point. レンダリングされた DOM 要素を操作する JS 相互運用呼び出しなどの、レンダリングされたコンテンツを使用した追加の初期化手順を行うには、この段階を使用します。Use this stage to perform additional initialization steps with the rendered content, such as JS interop calls that interact with the rendered DOM elements.

OnAfterRenderOnAfterRenderAsyncfirstRender パラメーター:The firstRender parameter for OnAfterRender and OnAfterRenderAsync:

  • コンポーネント インスタンスを初めて表示するときに true に設定されます。Is set to true the first time that the component instance is rendered.
  • 初期化作業が確実に 1 回だけ実行されるように使用できます。Can be used to ensure that initialization work is only performed once.

Pages/AfterRender.razor:Pages/AfterRender.razor:

@page "/after-render"
@using Microsoft.Extensions.Logging
@inject ILogger<AfterRender> Logger 

<button @onclick="LogInformation">Log information (and trigger a render)</button>

@code {
    private string message = "Initial assigned message.";

    protected override void OnAfterRender(bool firstRender)
    {
        Logger.LogInformation("OnAfterRender(1): firstRender: " +
            "{FirstRender}, message: {Message}", firstRender, message);

        if (firstRender)
        {
            message = "Executed for the first render.";
        }
        else
        {
            message = "Executed after the first render.";
        }

        Logger.LogInformation("OnAfterRender(2): firstRender: " +
            "{FirstRender}, message: {Message}", firstRender, message);
    }

    private void LogInformation()
    {
        Logger.LogInformation("LogInformation called");
    }
}
@page "/after-render"
@using Microsoft.Extensions.Logging
@inject ILogger<AfterRender> Logger 

<button @onclick="LogInformation">Log information (and trigger a render)</button>

@code {
    private string message = "Initial assigned message.";

    protected override void OnAfterRender(bool firstRender)
    {
        Logger.LogInformation("OnAfterRender(1): firstRender: " +
            "{FirstRender}, message: {Message}", firstRender, message);

        if (firstRender)
        {
            message = "Executed for the first render.";
        }
        else
        {
            message = "Executed after the first render.";
        }

        Logger.LogInformation("OnAfterRender(2): firstRender: " +
            "{FirstRender}, message: {Message}", firstRender, message);
    }

    private void LogInformation()
    {
        Logger.LogInformation("LogInformation called");
    }
}

OnAfterRenderAsync ライフサイクル イベント中に、レンダリング直後の非同期作業が発生する必要があります。Asynchronous work immediately after rendering must occur during the OnAfterRenderAsync lifecycle event:

protected override async Task OnAfterRenderAsync(bool firstRender)
{
    if (firstRender)
    {
        await ...
    }
}

OnAfterRenderAsync から Task を返した場合でも、フレームワークでは、そのタスクが完了しても、コンポーネントに対してさらにレンダリング サイクルがスケジュールされることはありません。Even if you return a Task from OnAfterRenderAsync, the framework doesn't schedule a further render cycle for your component once that task completes. これは、無限のレンダリング ループを回避するためです。This is to avoid an infinite render loop. これは、返されたTaskが完了するとレンダリング サイクルをさらにスケジュールする他のライフサイクル メソッドとは異なります。This is different from the other lifecycle methods, which schedule a further render cycle once a returned Task completes.

OnAfterRender および OnAfterRenderAsync " はサーバーでのプリレンダリング プロセス中には呼び出されません"。OnAfterRender and OnAfterRenderAsync aren't called during the prerendering process on the server. これらのメソッドは、プリレンダリングの後にコンポーネントが対話形式でレンダリングされるときに呼び出されます。The methods are called when the component is rendered interactively after prerendering. 次の場合に、アプリによりプリレンダリングされます。When the app prerenders:

  1. コンポーネントがサーバー上で実行され、HTTP 応答でいくつかの静的 HTML マークアップが生成される。The component executes on the server to produce some static HTML markup in the HTTP response. このフェーズでは、OnAfterRenderOnAfterRenderAsync は呼び出されません。During this phase, OnAfterRender and OnAfterRenderAsync aren't called.
  2. ブラウザーで Blazor スクリプト (blazor.webassembly.js または blazor.server.js) が起動すると、コンポーネントが対話型のレンダリング モードで再起動されます。When the Blazor script (blazor.webassembly.js or blazor.server.js) start in the browser, the component is restarted in an interactive rendering mode. コンポーネントが再起動されると、アプリはプリレンダリング フェーズでなくなるため、OnAfterRenderOnAfterRenderAsync が呼び出されますAfter a component is restarted, OnAfterRender and OnAfterRenderAsync are called because the app isn't in the prerendering phase any longer.

開発者コード内にイベント ハンドラーが提供されている場合、破棄時にこれらをアンフックします。If event handlers are provided in developer code, unhook them on disposal. 詳細については、「IDisposable を使用したコンポーネントの破棄」セクションを参照してください。For more information, see the Component disposal with IDisposable section.

UI 更新の抑制 (ShouldRender)Suppress UI refreshing (ShouldRender)

ShouldRender は、コンポーネントがレンダリングされるたびに呼び出されます。ShouldRender is called each time a component is rendered. ShouldRender をオーバーライドして、UI の更新を管理します。Override ShouldRender to manage UI refreshing. 実装によって true が返された場合は、UI が更新されます。If the implementation returns true, the UI is refreshed.

ShouldRender がオーバーライドされる場合でも、コンポーネントは常に最初にレンダリングされます。Even if ShouldRender is overridden, the component is always initially rendered.

Pages/ControlRender.razor:Pages/ControlRender.razor:

@page "/control-render"

<label>
    <input type="checkbox" @bind="shouldRender" />
    Should Render?
</label>

<p>Current count: @currentCount</p>

<p>
    <button @onclick="IncrementCount">Click me</button>
</p>

@code {
    private int currentCount = 0;
    private bool shouldRender = true;

    protected override bool ShouldRender()
    {
        return shouldRender;
    }

    private void IncrementCount()
    {
        currentCount++;
    }
}
@page "/control-render"

<label>
    <input type="checkbox" @bind="shouldRender" />
    Should Render?
</label>

<p>Current count: @currentCount</p>

<p>
    <button @onclick="IncrementCount">Click me</button>
</p>

@code {
    private int currentCount = 0;
    private bool shouldRender = true;

    protected override bool ShouldRender()
    {
        return shouldRender;
    }

    private void IncrementCount()
    {
        currentCount++;
    }
}

ShouldRender に関連するパフォーマンスのベスト プラクティスの詳細については、ASP.NET Core Blazor WebAssembly パフォーマンスに関するベスト プラクティスに関する記事を参照してください。For more information on performance best practices pertaining to ShouldRender, see ASP.NET Core Blazor WebAssembly パフォーマンスに関するベスト プラクティス.

状態変更 (StateHasChanged)State changes (StateHasChanged)

StateHasChanged は、状態が変更されたことをコンポーネントに通知します。StateHasChanged notifies the component that its state has changed. 必要に応じて、StateHasChanged を呼び出すと、コンポーネントが再レンダリングされます。When applicable, calling StateHasChanged causes the component to be rerendered.

StateHasChanged は、EventCallback メソッドに対して自動的に呼び出されます。StateHasChanged is called automatically for EventCallback methods. イベント コールバックの詳細については、ASP.NET Core Blazor のイベント処理に関する記事を参照してください。For more information on event callbacks, see ASP.NET Core Blazor のイベント処理.

コンポーネントのレンダリングと StateHasChanged を呼び出すタイミングの詳細については、ASP.NET Core Blazor コンポーネントのレンダリングに関する記事を参照してください。For more information on component rendering and when to call StateHasChanged, see ASP.NET Core Blazor コンポーネントのレンダリング.

レンダリング時の不完全な非同期アクションを処理するHandle incomplete async actions at render

ライフサイクル イベントで実行される非同期アクションは、コンポーネントがレンダリングされる前に完了していない可能性があります。Asynchronous actions performed in lifecycle events might not have completed before the component is rendered. ライフサイクル メソッドの実行中に、オブジェクトが null またはデータが不完全に設定されている可能性があります。Objects might be null or incompletely populated with data while the lifecycle method is executing. オブジェクトが初期化されていることを確認するレンダリング ロジックを提供します。Provide rendering logic to confirm that objects are initialized. オブジェクトが null の間、プレースホルダー UI 要素 (読み込みメッセージなど) をレンダリングします。Render placeholder UI elements (for example, a loading message) while objects are null.

Blazor テンプレートの FetchData コンポーネントでは、予測データ (forecasts) を非同期に受信するように、OnInitializedAsync がオーバーライドされます。In the FetchData component of the Blazor templates, OnInitializedAsync is overridden to asynchronously receive forecast data (forecasts). forecastsnull の場合、読み込みメッセージがユーザーに表示されます。When forecasts is null, a loading message is displayed to the user. OnInitializedAsync によって返された Task が完了すると、コンポーネントは更新された状態で再レンダリングされます。After the Task returned by OnInitializedAsync completes, the component is rerendered with the updated state.

Blazor Server テンプレートの Pages/FetchData.razor は以下のようになります。Pages/FetchData.razor in the Blazor Server template:

@page "/fetchdata"
@using BlazorSample.Data
@inject WeatherForecastService ForecastService

<h1>Weather forecast</h1>

<p>This component demonstrates fetching data from a service.</p>

@if (forecasts == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <table class="table">
        <!-- forecast data in table element content -->
    </table>
}

@code {
    private WeatherForecast[] forecasts;

    protected override async Task OnInitializedAsync()
    {
        forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
    }
}
@page "/fetchdata"
@using BlazorSample.Data
@inject WeatherForecastService ForecastService

<h1>Weather forecast</h1>

<p>This component demonstrates fetching data from a service.</p>

@if (forecasts == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <table class="table">
        <!-- forecast data in table element content -->
    </table>
}

@code {
    private WeatherForecast[] forecasts;

    protected override async Task OnInitializedAsync()
    {
        forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
    }
}

エラーの処理Handle errors

ライフサイクル メソッド実行中のエラー処理の詳細については、「ASP.NET Core Blazor アプリのエラーを処理する」を参照してください。For information on handling errors during lifecycle method execution, see ASP.NET Core Blazor アプリのエラーを処理する.

プリレンダリング後のステートフル再接続Stateful reconnection after prerendering

Blazor Server アプリで RenderModeServerPrerendered の場合、コンポーネントは最初にページの一部として静的にレンダリングされます。In a Blazor Server app when RenderMode is ServerPrerendered, the component is initially rendered statically as part of the page. ブラウザーがサーバーへの SignalR 接続を確立すると、コンポーネントが "再度" レンダリングされ、やりとりできるようになります。Once the browser establishes a SignalR connection back to the server, the component is rendered again and interactive. コンポーネントを初期化するための OnInitialized{Async} ライフサイクル メソッドが存在する場合、メソッドは "2 回" 実行されます。If the OnInitialized{Async} lifecycle method for initializing the component is present, the method is executed twice:

  • コンポーネントが静的にプリレンダリングされたとき。When the component is prerendered statically.
  • サーバー接続が確立された後。After the server connection has been established.

これにより、コンポーネントが最終的にレンダリングされるときに、UI に表示されるデータが大幅に変わる可能性があります。This can result in a noticeable change in the data displayed in the UI when the component is finally rendered. Blazor Server アプリでこの二重レンダリング動作を回避するには、プリレンダリング中に状態をキャッシュする識別子を渡し、プリレンダリング後に状態を取得します。To avoid this double-rendering behavior in a Blazor Server app, pass in an identifier to cache the state during prerendering and to retrieve the state after prerendering.

次のコードは、二重レンダリングを回避するテンプレートベースの Blazor Server アプリ内で更新される WeatherForecastService を示しています。The following code demonstrates an updated WeatherForecastService in a template-based Blazor Server app that avoids the double rendering. 次の例では、待機中の Delay (await Task.Delay(...)) は、GetForecastAsync メソッドからデータを返す前に短い遅延をシミュレートします。In the following example, the awaited Delay (await Task.Delay(...)) simulates a short delay before returning data from the GetForecastAsync method.

WeatherForecastService.cs:WeatherForecastService.cs:

using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Memory;
using BlazorSample.Shared;

public class WeatherForecastService
{
    private static readonly string[] summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild",
        "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };

    public WeatherForecastService(IMemoryCache memoryCache)
    {
        MemoryCache = memoryCache;
    }

    public IMemoryCache MemoryCache { get; }

    public Task<WeatherForecast[]> GetForecastAsync(DateTime startDate)
    {
        return MemoryCache.GetOrCreateAsync(startDate, async e =>
        {
            e.SetOptions(new MemoryCacheEntryOptions
            {
                AbsoluteExpirationRelativeToNow =
                    TimeSpan.FromSeconds(30)
            });

            var rng = new Random();

            await Task.Delay(TimeSpan.FromSeconds(10));

            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = startDate.AddDays(index),
                TemperatureC = rng.Next(-20, 55),
                Summary = summaries[rng.Next(summaries.Length)]
            }).ToArray();
        });
    }
}
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Memory;
using BlazorSample.Shared;

public class WeatherForecastService
{
    private static readonly string[] summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild",
        "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };

    public WeatherForecastService(IMemoryCache memoryCache)
    {
        MemoryCache = memoryCache;
    }

    public IMemoryCache MemoryCache { get; }

    public Task<WeatherForecast[]> GetForecastAsync(DateTime startDate)
    {
        return MemoryCache.GetOrCreateAsync(startDate, async e =>
        {
            e.SetOptions(new MemoryCacheEntryOptions
            {
                AbsoluteExpirationRelativeToNow =
                    TimeSpan.FromSeconds(30)
            });

            var rng = new Random();

            await Task.Delay(TimeSpan.FromSeconds(10));

            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = startDate.AddDays(index),
                TemperatureC = rng.Next(-20, 55),
                Summary = summaries[rng.Next(summaries.Length)]
            }).ToArray();
        });
    }
}

RenderMode の詳細については、「ASP.NET Core Blazor SignalR ガイダンス」を参照してください。For more information on the RenderMode, see ASP.NET Core Blazor SignalR ガイダンス.

このセクションのコンテンツでは、Blazor Server およびステートフルな SignalR の "再接続" に焦点を当てていますが、ホストされた Blazor WebAssembly アプリ (WebAssemblyPrerendered) でのプリレンダリングのシナリオでは、開発者コードを 2 回実行しないようにするための同様の条件とアプローチが必要です。Although the content in this section focuses on Blazor Server and stateful SignalR reconnection, the scenario for prerendering in hosted Blazor WebAssembly apps (WebAssemblyPrerendered) involves similar conditions and approaches to prevent executing developer code twice. ASP.NET Core 6.0 リリースでは、プリレンダリング中の初期化コードの実行の管理を向上させる "新しい状態保存機能" が計画されています。A new state preservation feature is planned for the ASP.NET Core 6.0 release that will improve the management of initialization code execution during prerendering.

アプリがプリレンダリングされていることを検出するDetect when the app is prerendering

このセクションは Razor コンポーネントをプリレンダリングする Blazor Server およびホストされている Blazor WebAssembly アプリに適用されます。This section applies to Blazor Server and hosted Blazor WebAssembly apps that prerender Razor components.

アプリでプリレンダリングするとき、JavaScript の呼び出しなどの特定のアクションは不可能です。While an app is prerendering, certain actions, such as calling into JavaScript, aren't possible. コンポーネントは、プリレンダリング時に異なるレンダリングが必要になる場合があります。Components may need to render differently when prerendered.

このような呼び出しの動作が保証されるまで JavaScript 相互運用呼び出しを遅延させるには、OnAfterRender{Async} ライフサイクル イベントをオーバーライドします。To delay JavaScript interop calls until a point where such calls are guaranteed to work, override the OnAfterRender{Async} lifecycle event. このイベントは、アプリが完全にレンダリングされた後にのみ呼び出されます。This event is only called after the app is fully rendered.

Pages/PrerenderedInterop1.razor:Pages/PrerenderedInterop1.razor:

@page "/prerendered-interop-1"
@using Microsoft.JSInterop
@inject IJSRuntime JSRuntime

<div @ref="divElement">Text during render</div>

@code {
    private ElementReference divElement;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await JSRuntime.InvokeVoidAsync(
                "setElementText1", divElement, "Text after render");
        }
    }
}
@page "/prerendered-interop-1"
@using Microsoft.JSInterop
@inject IJSRuntime JSRuntime

<div @ref="divElement">Text during render</div>

@code {
    private ElementReference divElement;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await JSRuntime.InvokeVoidAsync(
                "setElementText1", divElement, "Text after render");
        }
    }
}

前のコード例では、wwwroot/index.html (Blazor WebAssembly) または Pages/_Host.cshtml (Blazor Server) の <head> 要素内で、setElementText1 JavaScript 関数を提供します。For the preceding example code, provide a setElementText1 JavaScript function inside the <head> element of wwwroot/index.html (Blazor WebAssembly) or Pages/_Host.cshtml (Blazor Server). 関数は JSRuntimeExtensions.InvokeVoidAsync を指定して呼び出され、値を返しません。The function is called with JSRuntimeExtensions.InvokeVoidAsync and doesn't return a value:

<script>
  window.setElementText1 = (element, text) => element.innerText = text;
</script>

警告

前の例では、デモンストレーションのみを目的として、ドキュメント オブジェクト モデル (DOM) を直接変更しています。The preceding example modifies the Document Object Model (DOM) directly for demonstration purposes only. JavaScript での DOM の直接変更は、ほとんどのシナリオでは推奨されません。JavaScript が Blazor の変更追跡に影響する可能性があるためです。Directly modifying the DOM with JavaScript isn't recommended in most scenarios because JavaScript can interfere with Blazor's change tracking.

注意

前の例ではグローバル メソッドでクライアントが汚染されます。The preceding example pollutes the client with global methods. 実稼働アプリでのより適切なアプローチについては、「Blazor JavaScript の分離とオブジェクト参照」を参照してください。For a better approach in production apps, see Blazor JavaScript isolation and object references.

例:Example:

export setElementText1 = (element, text) => element.innerText = text;

次のコンポーネントは、プリレンダリングと互換性のある方法で、コンポーネントの初期化ロジックの一部として JavaScript の相互運用を使用する方法を示しています。The following component demonstrates how to use JavaScript interop as part of a component's initialization logic in a way that's compatible with prerendering. コンポーネントには、OnAfterRenderAsync 内からレンダリングの更新をトリガーできることが示されています。The component shows that it's possible to trigger a rendering update from inside OnAfterRenderAsync. 開発者はこのシナリオで無限ループを作成しないように注意する必要があります。The developer must be careful to avoid creating an infinite loop in this scenario.

JSRuntime.InvokeAsync が呼び出されるとき、ElementRef は、以前のライフサイクル メソッドではなく OnAfterRenderAsync でのみ使用されます。コンポーネントがレンダリングされるまで JavaScript 要素が存在しないためです。Where JSRuntime.InvokeAsync is called, ElementRef is only used in OnAfterRenderAsync and not in any earlier lifecycle method because there's no JavaScript element until after the component is rendered.

StateHasChanged は、JavaScript の相互運用呼び出しから取得された新しい状態でコンポーネントを再度レンダリングするために呼び出されます (詳細については、「ASP.NET Core Blazor コンポーネントのレンダリング」を参照してください)。StateHasChanged is called to rerender the component with the new state obtained from the JavaScript interop call (for more information, see ASP.NET Core Blazor コンポーネントのレンダリング). StateHasChangedinfoFromJsnull である場合にのみ呼び出されるため、このコードで無限ループが作成されることはありません。The code doesn't create an infinite loop because StateHasChanged is only called when infoFromJs is null.

Pages/PrerenderedInterop2.razor:Pages/PrerenderedInterop2.razor:

@page "/prerendered-interop-2"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JSRuntime

<p>
    Get value via JS interop call:
    <strong id="val-get-by-interop">@(infoFromJs ?? "No value yet")</strong>
</p>

<p>
    Set value via JS interop call:
</p>

<div id="val-set-by-interop" @ref="divElement"></div>

@code {
    private string infoFromJs;
    private ElementReference divElement;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender && infoFromJs == null)
        {
            infoFromJs = await JSRuntime.InvokeAsync<string>(
                "setElementText2", divElement, "Hello from interop call!");

            StateHasChanged();
        }
    }
}
@page "/prerendered-interop-2"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JSRuntime

<p>
    Get value via JS interop call:
    <strong id="val-get-by-interop">@(infoFromJs ?? "No value yet")</strong>
</p>

<p>
    Set value via JS interop call:
</p>

<div id="val-set-by-interop" @ref="divElement"></div>

@code {
    private string infoFromJs;
    private ElementReference divElement;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender && infoFromJs == null)
        {
            infoFromJs = await JSRuntime.InvokeAsync<string>(
                "setElementText2", divElement, "Hello from interop call!");

            StateHasChanged();
        }
    }
}

前のコード例では、wwwroot/index.html (Blazor WebAssembly) または Pages/_Host.cshtml (Blazor Server) の <head> 要素内で、setElementText2 JavaScript 関数を提供します。For the preceding example code, provide a setElementText2 JavaScript function inside the <head> element of wwwroot/index.html (Blazor WebAssembly) or Pages/_Host.cshtml (Blazor Server). 関数は IJSRuntime.InvokeAsync を指定して呼び出され、次の値を返します。The function is called withIJSRuntime.InvokeAsync and returns a value:

<script>
  window.setElementText2 = (element, text) => {
    element.innerText = text;
    return text;
  };
</script>

警告

前の例では、デモンストレーションのみを目的として、ドキュメント オブジェクト モデル (DOM) を直接変更しています。The preceding example modifies the Document Object Model (DOM) directly for demonstration purposes only. JavaScript での DOM の直接変更は、ほとんどのシナリオでは推奨されません。JavaScript が Blazor の変更追跡に影響する可能性があるためです。Directly modifying the DOM with JavaScript isn't recommended in most scenarios because JavaScript can interfere with Blazor's change tracking.

注意

前の例ではグローバル メソッドでクライアントが汚染されます。The preceding example pollutes the client with global methods. 実稼働アプリでのより適切なアプローチについては、「Blazor JavaScript の分離とオブジェクト参照」を参照してください。For a better approach in production apps, see Blazor JavaScript isolation and object references.

例:Example:

export setElementText2 = (element, text) => {
  element.innerText = text;
  return text;
};

IDisposable を使用したコンポーネントの破棄Component disposal with IDisposable

コンポーネントが IDisposable を実装している場合、そのコンポーネントが UI から削除されるときにフレームワークで破棄メソッドを呼び出し、アンマネージ リソースを解放することができます。If a component implements IDisposable, the framework calls the disposal method when the component is removed from the UI, where unmanaged resources can be released. 破棄は、コンポーネントの初期化中など、いつでも実行できます。Disposal can occur at any time, including during component initialization. 次のコンポーネントでは、@implements Razor ディレクティブを使用して IDisposable が実装されています。The following component implements IDisposable with the @implements Razor directive:

@using System
@implements IDisposable

...

@code {
    public void Dispose()
    {
        ...
    }
}

オブジェクトが破棄を必要とする場合、IDisposable.Dispose が呼び出されるきに、ラムダを使用してオブジェクトを破棄できます。If an object requires disposal, a lambda can be used to dispose of the object when IDisposable.Dispose is called. 次の例は ASP.NET Core Blazor コンポーネントのレンダリングの記事に登場し、Timer の破棄のためのラムダ式の使用を示しています。The following example appears in the ASP.NET Core Blazor コンポーネントのレンダリング article and demonstrates the use of a lambda expression for the disposal of a Timer.

Pages/CounterWithTimerDisposal.razor:Pages/CounterWithTimerDisposal.razor:

@page "/counter-with-timer-disposal"
@using System.Timers
@implements IDisposable

<h1>Counter with <code>Timer</code> disposal</h1>

<p>Current count: @currentCount</p>

@code {
    private int currentCount = 0;
    private Timer timer = new(1000);

    protected override void OnInitialized()
    {
        timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
        timer.Start();
    }

    private void OnTimerCallback()
    {
        _ = InvokeAsync(() =>
        {
            currentCount++;
            StateHasChanged();
        });
    }

    public void Dispose() => timer.Dispose();
}
@page "/counter-with-timer-disposal"
@using System.Timers
@implements IDisposable

<h1>Counter with <code>Timer</code> disposal</h1>

<p>Current count: @currentCount</p>

@code {
    private int currentCount = 0;
    private Timer timer = new Timer(1000);

    protected override void OnInitialized()
    {
        timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
        timer.Start();
    }

    private void OnTimerCallback()
    {
        _ = InvokeAsync(() =>
        {
            currentCount++;
            StateHasChanged();
        });
    }

    public void Dispose() => timer.Dispose();
}

非同期の破棄タスクの場合は、Dispose() の代わりに DisposeAsync を使用します。For asynchronous disposal tasks, use DisposeAsync instead of Dispose():

public async ValueTask DisposeAsync()
{
    await ...
}

注意

Dispose では、StateHasChanged の呼び出しはサポートされていません。Calling StateHasChanged in Dispose isn't supported. StateHasChanged は、レンダラーの破棄の一部として呼び出されることがあるため、その時点での UI 更新の要求はサポートされていません。StateHasChanged might be invoked as part of tearing down the renderer, so requesting UI updates at that point isn't supported.

.NET イベントからイベント ハンドラーのサブスクライブを解除します。Unsubscribe event handlers from .NET events. 次の Blazor フォームの例は、Dispose メソッドでイベント ハンドラーの登録を解除する方法を示しています。The following Blazor form examples show how to unsubscribe an event handler in the Dispose method:

  • プライベート フィールドとラムダのアプローチPrivate field and lambda approach

    @implements IDisposable
    
    <EditForm EditContext="@editContext">
    
        ...
    
        <button type="submit" disabled="@formInvalid">Submit</button>
    </EditForm>
    
    @code {
        // ...
    
        private EventHandler<FieldChangedEventArgs> fieldChanged;
    
        protected override void OnInitialized()
        {
            editContext = new(model);
    
            fieldChanged = (_, __) =>
            {
                // ...
            };
    
            editContext.OnFieldChanged += fieldChanged;
        }
    
        public void Dispose()
        {
            editContext.OnFieldChanged -= fieldChanged;
        }
    }
    
  • プライベート メソッドのアプローチPrivate method approach

    @implements IDisposable
    
    <EditForm EditContext="@editContext">
    
        ...
    
        <button type="submit" disabled="@formInvalid">Submit</button>
    </EditForm>
    
    @code {
        // ...
    
        protected override void OnInitialized()
        {
            editContext = new(model);
            editContext.OnFieldChanged += HandleFieldChanged;
        }
    
        private void HandleFieldChanged(object sender, FieldChangedEventArgs e)
        {
            // ...
        }
    
        public void Dispose()
        {
            editContext.OnFieldChanged -= HandleFieldChanged;
        }
    }
    
  • プライベート フィールドとラムダのアプローチPrivate field and lambda approach

    @implements IDisposable
    
    <EditForm EditContext="@editContext">
    
        ...
    
        <button type="submit" disabled="@formInvalid">Submit</button>
    </EditForm>
    
    @code {
        // ...
    
        private EventHandler<FieldChangedEventArgs> fieldChanged;
    
        protected override void OnInitialized()
        {
            editContext = new EditContext(model);
    
            fieldChanged = (_, __) =>
            {
                // ...
            };
    
            editContext.OnFieldChanged += fieldChanged;
        }
    
        public void Dispose()
        {
            editContext.OnFieldChanged -= fieldChanged;
        }
    }
    
  • プライベート メソッドのアプローチPrivate method approach

    @implements IDisposable
    
    <EditForm EditContext="@editContext">
    
        ...
    
        <button type="submit" disabled="@formInvalid">Submit</button>
    </EditForm>
    
    @code {
        // ...
    
        protected override void OnInitialized()
        {
            editContext = new EditContext(model);
            editContext.OnFieldChanged += HandleFieldChanged;
        }
    
        private void HandleFieldChanged(object sender, FieldChangedEventArgs e)
        {
            // ...
        }
    
        public void Dispose()
        {
            editContext.OnFieldChanged -= HandleFieldChanged;
        }
    }
    

匿名関数、メソッド、または式が使用されている場合、IDisposable の実装やデリゲートの登録解除を行う必要はありません。When anonymous functions, methods, or expressions, are used, it isn't necessary to implement IDisposable and unsubscribe delegates. しかし、デリゲートの登録解除に失敗することは、イベントを公開するオブジェクトが、デリゲートを登録するコンポーネントの有効期間を超えている場合 に問題になります。However, failing to unsubscribe a delegate is a problem when the object exposing the event outlives the lifetime of the component registering the delegate. この場合、登録されたデリゲートによって元のオブジェクトが保持されているため、メモリ リークが発生します。When this occurs, a memory leak results because the registered delegate keeps the original object alive. そのため、イベント デリゲートがすぐに破棄されることがわかっている場合にのみ、次のアプローチを使用してください。Therefore, only use the following approaches when you know that the event delegate disposes quickly. 破棄が必要なオブジェクトの有効期間が不明な場合は、前の例で示したように、デリゲート メソッドを登録し、そのデリゲートを適切に破棄します。When in doubt about the lifetime of objects that require disposal, subscribe a delegate method and properly dispose the delegate as the earlier examples show.

  • 匿名のラムダ メソッドのアプローチ (明示的な破棄は不要):Anonymous lambda method approach (explicit disposal not required):

    private void HandleFieldChanged(object sender, FieldChangedEventArgs e)
    {
        formInvalid = !editContext.Validate();
        StateHasChanged();
    }
    
    protected override void OnInitialized()
    {
        editContext = new(starship);
        editContext.OnFieldChanged += (s, e) => HandleFieldChanged((editContext)s, e);
    }
    
    private void HandleFieldChanged(object sender, FieldChangedEventArgs e)
    {
        formInvalid = !editContext.Validate();
        StateHasChanged();
    }
    
    protected override void OnInitialized()
    {
        editContext = new EditContext(starship);
        editContext.OnFieldChanged += (s, e) => HandleFieldChanged((editContext)s, e);
    }
    
  • 匿名のラムダ式のアプローチ (明示的な破棄は不要):Anonymous lambda expression approach (explicit disposal not required):

    private ValidationMessageStore messageStore;
    
    [CascadingParameter]
    private EditContext CurrentEditContext { get; set; }
    
    protected override void OnInitialized()
    {
        ...
    
        messageStore = new(CurrentEditContext);
    
        CurrentEditContext.OnValidationRequested += (s, e) => messageStore.Clear();
        CurrentEditContext.OnFieldChanged += (s, e) => 
            messageStore.Clear(e.FieldIdentifier);
    }
    
    private ValidationMessageStore messageStore;
    
    [CascadingParameter]
    private EditContext CurrentEditContext { get; set; }
    
    protected override void OnInitialized()
    {
        ...
    
        messageStore = new ValidationMessageStore(CurrentEditContext);
    
        CurrentEditContext.OnValidationRequested += (s, e) => messageStore.Clear();
        CurrentEditContext.OnFieldChanged += (s, e) => 
            messageStore.Clear(e.FieldIdentifier);
    }
    

    匿名ラムダ式を使用した上記のコードの完全な例は、ASP.NET Core Blazor のフォームと検証の記事に示されています。The full example of the preceding code with anonymous lambda expressions appears in the ASP.NET Core Blazor のフォームと検証 article.

詳細については、「アンマネージ リソースのクリーンアップ」と、その後に続く Dispose および DisposeAsync メソッドの実装に関するトピックを参照してください。For more information, see Cleaning up unmanaged resources and the topics that follow it on implementing the Dispose and DisposeAsync methods.

取り消し可能なバックグラウンド作業Cancelable background work

ネットワーク呼び出し (HttpClient) の実行やデータベースとの対話など、コンポーネントによって実行時間の長いバックグラウンド作業が実行されることがよくあります。Components often perform long-running background work, such as making network calls (HttpClient) and interacting with databases. いくつかの状況でシステム リソースを節約するために、バックグラウンド作業を停止することをお勧めします。It's desirable to stop the background work to conserve system resources in several situations. たとえば、ユーザーがコンポーネントの操作を止めても、バックグラウンドの非同期操作は自動的に停止しません。For example, background asynchronous operations don't automatically stop when a user navigates away from a component.

バックグラウンド作業項目の取り消しが必要になるその他の理由には、次のようなものがあります。Other reasons why background work items might require cancellation include:

  • 実行中のバックグラウンド タスクは、不完全な入力データまたは処理パラメーターを使用して開始されました。An executing background task was started with faulty input data or processing parameters.
  • 現在実行中のバックグラウンド作業項目のセットを、新しい作業項目のセットに置き換える必要があります。The current set of executing background work items must be replaced with a new set of work items.
  • 現在実行中のタスクの優先度を変更する必要があります。The priority of currently executing tasks must be changed.
  • サーバーを再展開するには、アプリをシャットダウンする必要があります。The app must be shut down for server redeployment.
  • サーバー リソースが制限され、バックグラウンド作業項目の再スケジュールが必要になりました。Server resources become limited, necessitating the rescheduling of background work items.

コンポーネントに取り消し可能なバックグラウンド作業パターンを実装するには:To implement a cancelable background work pattern in a component:

次に例を示します。In the following example:

  • await Task.Delay(5000, cts.Token); は、実行時間が長い非同期のバックグラウンド作業を表します。await Task.Delay(5000, cts.Token); represents long-running asynchronous background work.
  • BackgroundResourceMethod は、メソッドが呼び出される前に Resource が破棄された場合に開始されない、実行時間が長いバックグラウンド メソッドを表します。BackgroundResourceMethod represents a long-running background method that shouldn't start if the Resource is disposed before the method is called.

Pages/BackgroundWork.razor:Pages/BackgroundWork.razor:

@page "/background-work"
@implements IDisposable
@using System.Threading
@using Microsoft.Extensions.Logging
@inject ILogger<BackgroundWork> Logger

<button @onclick="LongRunningWork">Trigger long running work</button>
<button @onclick="Dispose">Trigger Disposal</button>

@code {
    private Resource resource = new();
    private CancellationTokenSource cts = new();

    protected async Task LongRunningWork()
    {
        Logger.LogInformation("Long running work started");

        await Task.Delay(5000, cts.Token);

        cts.Token.ThrowIfCancellationRequested();
        resource.BackgroundResourceMethod(Logger);
    }

    public void Dispose()
    {
        Logger.LogInformation("Executing Dispose");
        cts.Cancel();
        cts.Dispose();
        resource?.Dispose();
    }

    private class Resource : IDisposable
    {
        private bool disposed;

        public void BackgroundResourceMethod(ILogger<BackgroundWork> logger)
        {
            logger.LogInformation("BackgroundResourceMethod: Start method");

            if (disposed)
            {
                logger.LogInformation("BackgroundResourceMethod: Disposed");
                throw new ObjectDisposedException(nameof(Resource));
            }

            // Take action on the Resource

            logger.LogInformation("BackgroundResourceMethod: Action on Resource");
        }

        public void Dispose()
        {
            disposed = true;
        }
    }
}
@page "/background-work"
@implements IDisposable
@using System.Threading
@using Microsoft.Extensions.Logging
@inject ILogger<BackgroundWork> Logger

<button @onclick="LongRunningWork">Trigger long running work</button>
<button @onclick="Dispose">Trigger Disposal</button>

@code {
    private Resource resource = new Resource();
    private CancellationTokenSource cts = new CancellationTokenSource();

    protected async Task LongRunningWork()
    {
        Logger.LogInformation("Long running work started");

        await Task.Delay(5000, cts.Token);

        cts.Token.ThrowIfCancellationRequested();
        resource.BackgroundResourceMethod(Logger);
    }

    public void Dispose()
    {
        Logger.LogInformation("Executing Dispose");
        cts.Cancel();
        cts.Dispose();
        resource?.Dispose();
    }

    private class Resource : IDisposable
    {
        private bool disposed;

        public void BackgroundResourceMethod(ILogger<BackgroundWork> logger)
        {
            logger.LogInformation("BackgroundResourceMethod: Start method");

            if (disposed)
            {
                logger.LogInformation("BackgroundResourceMethod: Disposed");
                throw new ObjectDisposedException(nameof(Resource));
            }

            // Take action on the Resource

            logger.LogInformation("BackgroundResourceMethod: Action on Resource");
        }

        public void Dispose()
        {
            disposed = true;
        }
    }
}

Blazor Server 再接続イベントBlazor Server reconnection events

この記事で説明するコンポーネント ライフサイクル イベントは、Blazor Server の再接続イベント ハンドラーとは別々に動作します。The component lifecycle events covered in this article operate separately from Blazor Server's reconnection event handlers. Blazor Server アプリとクライアントの SignalR 接続が失われた場合には、UI の更新だけが中断されます。When a Blazor Server app loses its SignalR connection to the client, only UI updates are interrupted. その接続が再確立されると、UI の更新が再開されます。UI updates are resumed when the connection is re-established. 回線ハンドラーのイベントと構成の詳細については、「ASP.NET Core Blazor SignalR ガイダンス」を参照してください。For more information on circuit handler events and configuration, see ASP.NET Core Blazor SignalR ガイダンス.