Поделиться через


Blazor Обзор управления состоянием ядра ASP.NET

В этой статье и других статьях в этом узле описываются распространенные подходы к поддержанию данных пользователя (состояния) во время использования приложения и между сеансами браузера, в том числе во время предварительной подготовки сервера.

Типичным требованием во время Blazor разработки приложений является общий доступ к состоянию между компонентами:

  • Родительский элемент дочерним: родительский компонент передает состояние дочернему компоненту с помощью параметров.
  • Дочерний к родителю: дочерний компонент включает привязку данных к его состоянию или предоставляет состояние через обратные вызовы.
  • Родительский для потомков: родительское состояние совместно с всеми потомками с использованием каскадных значений.
  • На уровне приложения: состояние совместно используется для всего приложения с помощью настроенных служб состояний приложений.
  • На канал: для определенного канала используется состояние с использованием служб состояний приложений с областью действия.

Сохраняемое состояние может потребоваться для выживания обновлений страниц, возобновления каналов и предварительной подготовки. Для состояния часто требуется централизованное управление, отслеживание и тестирование. Расположения и методы сохранения состояния являются высокой переменной.

Blazor не обеспечивает комплексное, с точки зрения управления состояниями. Сторонние продукты и службы контейнера состояния, которые легко работают с Blazorтакими продуктами, как Flux, Redux и MobX, удовлетворяют практически любому требованию приложения.

Оставшаяся часть этой статьи описывает общие стратегии управления состоянием для любого типа Blazor приложения.

Управление состоянием с помощью URL-адреса

Для временных данных, представляющих состояние навигации, моделируют данные как часть URL-адреса. Ниже приведены примеры данных о состоянии пользователя, которые моделируются в URL-адресе.

  • Идентификатор просматриваемой сущности.
  • Номер текущей страницы в сетке, разбитой на страницы.

Содержимое адресной строки браузера будет сохраняться в следующих случаях.

  • Если пользователь вручную обновляет страницу.
  • Только в сценариях на стороне сервера: если веб-сервер становится недоступным, и пользователь вынужден перезагрузить страницу, чтобы подключиться к другому серверу.

Сведения о том, как определить шаблоны URL-адресов с помощью директивы @page, см. в разделе ASP.NET Core МаршрутизацияBlazor.

Служба контейнера данных в оперативной памяти

Вложенные компоненты обычно привязываются к данным с помощью цепочки привязки, как описано в статье Привязка к данным в ASP.NET Core Blazor. Вложенные и невложенные компоненты могут иметь общий доступ к данным с помощью зарегистрированного контейнера состояния в памяти. Пользовательский класс контейнера состояния может использовать назначаемый Action, чтобы уведомлять компоненты в других частях приложения об изменениях состояния. В следующем примере :

  • Пара компонентов использует контейнер состояния для отслеживания свойства.
  • Один из компонентов в следующем примере вложен в другой компонент, но для использования этого подхода вложение не требуется.

Это важно

В этом разделе показано, как создать службу контейнера состояния в памяти, зарегистрировать службу и использовать службу в компонентах. Пример не сохраняет данные без дальнейшей доработки. Для постоянного хранения данных контейнер состояния должен принять базовый механизм хранения, который сохраняется при очистке памяти браузера. Это можно сделать с помощью localStorage/sessionStorage или другой технологии.

StateContainer.cs:

public class StateContainer
{
    private string? savedString;

    public string Property
    {
        get => savedString ?? string.Empty;
        set
        {
            savedString = value;
            NotifyStateChanged();
        }
    }

    public event Action? OnChange;

    private void NotifyStateChanged() => OnChange?.Invoke();
}

Клиентские приложения (Program файл):

builder.Services.AddSingleton<StateContainer>();

Серверные приложения (Program файл, ASP.NET Core в .NET 6 или более поздней версии):

builder.Services.AddScoped<StateContainer>();

Серверные приложения (Startup.ConfigureServices обычно в .NET 6 или более ранних версиях Startup.cs):

services.AddScoped<StateContainer>();

Shared/Nested.razor:

@implements IDisposable
@inject StateContainer StateContainer

<h2>Nested component</h2>

<p>Nested component Property: <b>@StateContainer.Property</b></p>

<p>
    <button @onclick="ChangePropertyValue">
        Change the Property from the Nested component
    </button>
</p>

@code {
    protected override void OnInitialized()
    {
        StateContainer.OnChange += StateHasChanged;
    }

    private void ChangePropertyValue()
    {
        StateContainer.Property = 
            $"New value set in the Nested component: {DateTime.Now}";
    }

    public void Dispose()
    {
        StateContainer.OnChange -= StateHasChanged;
    }
}

StateContainerExample.razor:

@page "/state-container-example"
@implements IDisposable
@inject StateContainer StateContainer

<h1>State Container Example component</h1>

<p>State Container component Property: <b>@StateContainer.Property</b></p>

<p>
    <button @onclick="ChangePropertyValue">
        Change the Property from the State Container Example component
    </button>
</p>

<Nested />

@code {
    protected override void OnInitialized()
    {
        StateContainer.OnChange += StateHasChanged;
    }

    private void ChangePropertyValue()
    {
        StateContainer.Property = "New value set in the State " +
            $"Container Example component: {DateTime.Now}";
    }

    public void Dispose()
    {
        StateContainer.OnChange -= StateHasChanged;
    }
}

Предыдущие компоненты реализуют IDisposable, а делегаты OnChange отписываются в методах Dispose, которые вызываются фреймворком при удалении компонентов. Дополнительные сведения см. в разделе ASP.NET Core Razor утилизации компонентов.

Каскадные значения и параметры

Используйте каскадные значения и параметры для управления состоянием путем потоков данных из компонента предка Razor в компоненты потомков:

  • Для потребления состояния во множестве компонентов.
  • Если имеется только один объект состояния верхнего уровня для сохранения.

Каскадные значения корневого уровня с CascadingValueSource<TValue> разрешают Razor уведомления подписчика компонента об измененных каскадных значениях. Дополнительные сведения и рабочий пример см. в примере NotifyingDalekASP.NET Core Blazor каскадных значений и параметров.

Поддержка изменений состояния извне Blazorконтекста синхронизации

При использовании пользовательской службы управления состояниями, в которой требуется поддерживать изменения состояния вне контекста синхронизации Blazor(например, из таймера или фоновой службы), все используемые компоненты должны упаковать вызов StateHasChanged в ComponentBase.InvokeAsync. Это гарантирует, что уведомление об изменении обрабатывается в контексте синхронизации отрисовщика.

Если служба управления состоянием не вызывает StateHasChanged в контексте синхронизации объекта Blazor, возникает следующая ошибка:

System.InvalidOperationException: 'Текущий поток не связан с диспетчером. Используйте InvokeAsync(), чтобы переключить выполнение на Dispatcher при активации рендеринга или состояния компонента.

Дополнительные сведения и пример устранения этой ошибки см. в статье рендеринг компонентов ASP.NET Core .