MojoDK
MojoDK

Reputation: 4538

In Blazor .NET 8 , where do I load and store user details?

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

Answers (2)

XieMan
XieMan

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

enter image description here

Upvotes: 2

Mayur Ekbote
Mayur Ekbote

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

Related Questions