In blazor webassembly, I have a singleton service containing this property:
public IsAuthenticatedModel Auth { get; set; } = null;
I set this property in Login page:
await genericService.SetSession<IsAuthenticatedModel>("AuthModel", ViewData.AuthModel);
mzSingleton.Auth = ViewData.AuthModel;
First I save the data in the server session then I store it in the singleton service that I can access in the app.
In the following code in App.razor, I check if the Singleton property is null then fill it with the data comming from server's session:
protected override async Task OnInitializedAsync()
{
if (mzSingleton.Auth == null)
mzSingleton.Auth = await genericService.GetSession<IsAuthenticatedModel>("AuthModel")
?? new IsAuthenticatedModel();
}
I do all of this because I want to keep service's data after refreshing the page.
Unfortunately this code does not work immediately after refresh. when I access the singleton service in the counter page, I have to go to another page and comeback to for it to work (Once I read, in new versions of blazor component rendering is from child to parent).
Is there a workaound for this problem other than cascading parameters? I use .Net 6.
EDIT:
in the Login page i set the service property:
await genericService.SetSession<IsAuthenticatedModel>("AuthModel", AuthModel);
mzSingleton.Auth = AuthModel;
in the Counter.razor page:
@if (mzSingleton.Auth != null)
{
<h1>2 (serverside session)</h1>
<label>Is Authenticated: </label><label>@mzSingleton.Auth.IsAuthenticated</label>
<label>User: </label><label>@mzSingleton.Auth.User</label>
@if (mzSingleton.Auth.Roles?.Count > 0)
{
foreach (string role in mzSingleton.Auth.Roles)
{
<label>Role: </label><label>@role</label>
}
}
}
As I said after refreshing counter page the mzSingleton.Auth has no value, But after doing something (when statehaschanged is fired) the page shows Auth values correctly!
EDIT2:
Since I get two objects from server's session I reveresed session calls order and this time my Auth service worked and my other service NOT:
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
mzSingleton.Auth = await genericService.GetSession<IsAuthenticatedModel>("AuthModel");
mzSingleton.MyBooz = await genericService.GetSession<Booz>("Booz");
StateHasChanged();
}
So I suspected to 'async' so I replaced OnInitializedAsync to OnInitialized the synchronous version. So I had to remove await and add result like this:
genericService.GetSession<IsAuthenticatedModel>("AuthModel").Result;
But I get a Blazor error that says: "Cannot wait on monitors on this runtime.", So I forced to change the genericservice methods to synchronous versions:
public async Task<T> GetSession<T>(string key) where T : class, new()
to
public T GetSession<T>(string key) where T : class, new()
But inside this method I use:
await httpClient.GetJsonAsync<SessionKeyValue>($"api/Session/GetSession/{key}");
If I remove await and Use .Result at the end I get the same error. and if I want to use non async .GetJson() There is no such method in Blazor's HttpClient. So I locked to use Async methods. And if there are two Async calls in OnInitializedAsync one of them executes with delay, And is not ready after page refresh!
CodePudding user response:
I solved my problem with this trick: first I defined ServicesSet property in the singleton service:
public class MzSingleton
{
public Booz MyBooz { get; set; }
public IsAuthenticatedModel Auth { get; set; }
public Action ServicesSet { get; set; }
}
Then in the App.razor:
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
mzSingleton.Auth = await genericService.GetSession<IsAuthenticatedModel>("AuthModel");
mzSingleton.MyBooz = await genericService.GetSession<Booz>("Booz");
mzSingleton.ServicesSet?.Invoke();
}
And in Counter.razor:
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
mzSingleton.ServicesSet = () => StateHasChanged();
}