Reputation: 4538
In ASP.NET MVC, I handled the session_start
event to load user details and store them in a session variable, so I didn't have to query the database with each page load.
I'm trying to figure out how to do this in Blazor on .NET 8.
I seams like the App.razor
is a good place to put my code, since it is executed with each page load - but I don't know if it is the best practice?
My code in App.razor
(server code) looks like this (I'm using Windows authentication):
@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);
}
}
}
But I get an error:
InvalidOperationException: JavaScript interop calls cannot be issued at this time.
How do you guys solve this? Do you query the database for the same info on each page load?
Upvotes: 0
Views: 652
Reputation: 378
1.you can't write code in app.razor or mainlayout.razor to implement this function
2.you can write in the page home.razor Use whether httpcontext is Null to determine whether it is the first time to load,but you should write every page
3.you can use In-memory state container service,Here's an example:
1). create a class :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). config in program.cs
builder.Services.AddScoped<StateContainer>();Server-side
builder.Services.AddSingleton<StateContainer>();//Client-side
3).create a 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).create a demo to use 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).The results are as follows
Upvotes: 2
Reputation: 2080
JavaScript calls are available only after the component is completely initialized. Hence, calls to interop should be made only after the rendering is complete. i.e. anytime after OnAfterRender is invoked and it is not a first render.
Upvotes: 1