Search code examples
asp.netblazorsession-storage

Blazor ProtectedSessionStorage timeout


Hey I´m currently working on a blazor-server-app and encountered a problem.

I´m using the protected session storage to save my data. If the value of a key exceeds a certain amount of characters (34160) the circuit starts attempt to reconnect and recovers. However the application is in a frozen state.

I try to access the storage inside the OnInitializedAsync method.

 protected override async Task OnInitializedAsync()
 {
    Warenkorb = await SessionStorageService.GetNotNull<Warenkorb>( SessionStorageKeys.WarenkorbKey );
 }

Inside the SessionStorageService:

 public async Task<T> GetNotNull<T>( string key )
 {
    ProtectedBrowserStorageResult<string> result = await protectedSessionStorage.GetAsync<string>( key );
 }

Debugger stops at "GetAsync(key)"

Is there any explanation for this? Thanks


Solution

  • I could not find an explanation, but since I faced same problem I ended up with next workaround:

    Create a Session class with a Guid Id and any other properties you might need as session state:

    public class Session
        {
            public Guid Id { get; set; }
            public int Counter { get; set; }
        }
    

    Create an application state class to maintain a collection of Sessions:

    public class AppState
    {
        public List<Session> Sessions { get; set; } = new();
    
        public Session AddSession() {
            var retval = new Session();
            Sessions.Add(retval);
            return retval;
        }
    
        public Session? Session(Guid? sessionId) => Sessions.FirstOrDefault(x => x.Guid == sessionId);
    }
    

    Register it on Program.cs as a Singleton service:

    var state = new TestState.Shared.AppState(http);
    builder.Services.AddSingleton(sp => state);
    

    Create a SessionProvider component which will create a new session n AppState if missing and persist it's Id on the Protected Session Storage:

    @using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
    @inject ProtectedSessionStorage ProtectedSessionStore
    @inject AppState AppState
    
    @if (isLoaded)
    {
        <CascadingValue Value="@this">
            @ChildContent
        </CascadingValue>
    }
    else
    {
        <p>Loading...</p>
    }
    
    @code {
        [Parameter] public RenderFragment? ChildContent { get; set; }
        public Session? Session { get; set; }
        public bool isLoaded;
    
        protected override async Task OnAfterRenderAsync(bool firstRender)
        {
            if (firstRender)
            {
                var result = await ProtectedSessionStore.GetAsync<Guid>("sessionId");
                if (result.Success)
                    Session = AppState.Session(result.Value);
                else
                {
                    Session = AppState.AddSession();
                    await ProtectedSessionStore.SetAsync("sessionId", Session.Guid);
                }
                isLoaded = true;
                StateHasChanged();
            }
        }
    }
    

    Wrap App.razor Router component with SessionProvider:

    <SessionProvider>
        <Router AppAssembly="@typeof(App).Assembly">
            <Found Context="routeData">
                <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
                <FocusOnNavigate RouteData="@routeData" Selector="h1" />
            </Found>
            <NotFound>
                <PageTitle>Not found</PageTitle>
                <LayoutView Layout="@typeof(MainLayout)">
                    <p role="alert">Sorry, there's nothing at this address.</p>
                </LayoutView>
            </NotFound>
        </Router>
    </SessionProvider>
    

    Access Session properties from any page:

    @page "/counter"
    
    <PageTitle>Counter</PageTitle>
    
    <h1>Counter</h1>
    
    <div>session id: @sessionProvider?.Session?.Id.ToString()</div>
    
    <p role="status">Current count: @sessionProvider?.Session?.Counter</p>
    
    <button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
    
    @code {
        [CascadingParameter] public SessionProvider? sessionProvider { get; set; }
    
        private void IncrementCount()
        {
            if (sessionProvider?.Session != null)
                sessionProvider.Session.Counter++;
        }
    
    }