В ASP.NET MVC я обработал событие session_start
для загрузки сведений о пользователе и сохранения их в переменной сеанса, поэтому мне не приходилось запрашивать базу данных при каждой загрузке страницы.
Я пытаюсь понять, как это сделать в Blazor на .NET 8.
Мне кажется, что App.razor
— хорошее место для размещения моего кода, поскольку он выполняется при каждой загрузке страницы, но я не знаю, является ли это лучшей практикой?
Мой код в App.razor
(код сервера) выглядит следующим образом (я использую аутентификацию Windows):
@code {
protected override async Task OnInitializedAsync()
{
UserDetails details;
var user = (await AuthenticationStateProvider.GetAuthenticationStateAsync()).User;
var userId = user.Identity?.Name?.ToLower();
var result = await BrowserStorage.GetAsync<UserDetails>(userId);
if (result.Success)
{
details = (UserDetails)result.Value;
}
else
{
GetUserDetailsQuery query = new GetUserDetailsQuery();
query.UserLogin = await getUserId();
details = query.Handle();
await BrowserStorage.SetAsync(userId, details);
}
}
}
Но я получаю ошибку:
InvalidOperationException: в настоящее время невозможно выполнить вызовы взаимодействия JavaScript.
Как вы, ребята, это решаете? Вы запрашиваете у базы данных одну и ту же информацию при каждой загрузке страницы?
Вызовы JavaScript доступны только после полной инициализации компонента. Следовательно, вызовы взаимодействия следует выполнять только после завершения рендеринга. то есть в любое время после вызова OnAfterRender, и это не первый рендеринг.
1. вы не можете написать код в app.razor или mainlayout.razor для реализации этой функции.
2. вы можете написать на странице home.razor. Используйте значение httpcontext, равное нулю, чтобы определить, загружается ли он в первый раз, но вы должны писать каждую страницу.
3. вы можете использовать службу контейнеров состояния в памяти. Вот пример:
1). создайте класс: StateContainer.cs
namespace StoreDemo
{
public class StateContainer
{
private string? savedString;
public string UserId
{
get => savedString ?? string.Empty;
set
{
savedString = value;
NotifyStateChanged();
}
}
public event Action? OnChange;
private void NotifyStateChanged() => OnChange?.Invoke();
}
}
2). конфигурация в program.cs
builder.Services.AddScoped<StateContainer>();Server-side
builder.Services.AddSingleton<StateContainer>();//Client-side
3). Создайте Nested.razor:
@implements IDisposable
@inject StateContainer StateContainer
@rendermode InteractiveServer
<h2>userId from children: @StateContainer.UserId</h2>
@code {
[CascadingParameter]
public HttpContext httpContext { get; set; }
private UserDetails details;
protected override void OnInitialized()
{
StateContainer.OnChange += StateHasChanged;
}
protected override async Task OnInitializedAsync()
{
if (httpContext is null)
{
StateContainer.OnChange += StateHasChanged;
var result = await ProtectedLocalStorage.GetAsync<UserDetails>("userId");
if (result.Success)
{
details = result.Value;
StateContainer.UserId = details.userId.ToString();
}
else
{
details = new UserDetails
{
userId = "11",
userName = "hua"
};
await ProtectedLocalStorage.SetAsync("userId", details);
}
}
}
public void Dispose()
{
StateContainer.OnChange -= StateHasChanged;
}
}
4). Создайте демо-версию для использования Nested.razor.
@page "/state-container-example"
@inject StateContainer StateContainer
@rendermode InteractiveServer
<h2>userId from parent :@StateContainer.UserId</h2>
<Nested />
@code {
[CascadingParameter]
public HttpContext httpContext { get; set; }
private string UserId { get; set; }
protected override void OnInitialized()
{
StateContainer.OnChange += StateHasChanged;
}
}
5).Результаты следующие:
Помните, что в Blazor «загрузка страницы» — довольно редкая вещь. На самом деле это для навигации по страницам, чего технически вам НИКОГДА не нужно делать. Но есть много способов справиться с этим. Прежде всего, если ваша БД находится на вашем сервере, просто перезагрузка информации при каждой навигации будет очень незначительной. Но если вы хотите, вы можете использовать службу Scoped или Singleton для предварительной загрузки данных пользователя и предоставления их на любой странице, которая внедряет службу. Если вам нужны настройки для каждого устройства, вы можете использовать локальное хранилище. Для чего-то такого простого я мог бы просто загрузить информацию о пользователе на странице макета.