Security initializing after page load

Hello Radzen Team!

My pages have dozens of conditionals based on the user role. Sometimes the conditionals fail because the Security.InitializeAsync method runs after the page loads. This can be achieved by refreshing the page multiple times very quickly.

After analysing the generated code, I noticed the layouts don't have this problem because this method is called inside the Page.OnInitializedAsyncmethod:

protected override async System.Threading.Tasks.Task OnInitializedAsync()
        {
             if (Security != null)
             {
                  Security.Authenticated += Authenticated;
                  await Security.InitializeAsync(AuthenticationStateProvider);
             }
             await Load();
        }

In the meantime, all the pages look like this:

protected override async System.Threading.Tasks.Task OnInitializedAsync()
        {
            if (!Security.IsAuthenticated())
            {
                UriHelper.NavigateTo("Login", true);
            }
            else
            {
                await Load();
            }
        }

Inserting the Security.InitializeAsync in the Page.OnInitializedAsync fixed my issue, but I wonder if Radzen could implement this by default?

This shouldn't be the case though. The code in the layout is supposed to execute before the code in the page (or at least it used to in .NET Core 3). Can you provide an example which fails?

This is how we implemented it in a certain Radzen version. Then we realised that we also need the initialization code in the Layout (otherwise users won't be able to use the Security service in a layout). Then in .NET 5 we found out that an exception is thrown very often when refreshing the page (seems to work fine in .NET 3.1 which is probably what you are using).

It is thrown because obviously in .NET 5 the layout and page execute in different threads. The exception is thrown by the AspNetCore UserManager when retrieving the current user.

Long story short this is why we removed this code from the Page load event of Blazor server applications. We will see if we can find a way to circumvent that exception.

My app is running .NET Core 3.1. I was about to give an example until I saw the 2.56.2 update, which seems to have fixed all these problems. Thank you very much!

Yes, I have seen this exception before in .NET Core 3.1 also. From my testing, it was related to the Principal Factory method locks, which prevents multiple method calls (in my case, the FindByNameAsync).

Indeed we think we fixed this problem by adding a Semaphore to the InitializeAsync method. It is now invoked in both the Layout and Page.

1 Like

I realize this is several versions and a couple years later, but I am using conditionals based on user role very similar to the OP and I am running into this issue on version 2.68. I am using DotNet 5.

For context, I am trying to call user details in a data grid column. If the page is refreshed manually, the grid breaks and This issue arises. Refreshing from a different page and then navigating back resolves the issue until the next manual refresh. I have this issue on any page with a grid that calls user data.

Console Log after manual page refresh:

dotnet: warn: Microsoft.AspNetCore.Components.Server.Circuits.RemoteRenderer[100]
      Unhandled exception rendering component: Value cannot be null. (Parameter 'source')
      System.ArgumentNullException: Value cannot be null. (Parameter 'source')
         at System.Linq.ThrowHelper.ThrowArgumentNullException(ExceptionArgument argument)
         at System.Linq.Enumerable.Where[TSource](IEnumerable`1 source, Func`2 predicate)
         at BiaBuilder.Pages.Branch.<>c__DisplayClass0_1.<BuildRenderTree>b__11(RenderTreeBuilder __builder4) in C:\Users\Fellblade\Desktop\BIABuilder\BIA Builder\server\Pages\Branch.razor:line 44
         at Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder.AddContent(Int32 sequence, RenderFragment fragment)
         at Radzen.Blazor.RadzenDataGridRow`1.<>c__DisplayClass0_0.<BuildRenderTree>b__1(RenderTreeBuilder __builder2)
         at Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder.AddContent(Int32 sequence, RenderFragment fragment)
         at Radzen.Blazor.RadzenDataGridCell`1.<BuildRenderTree>b__0_2(RenderTreeBuilder __builder4)
         at Microsoft.AspNetCore.Components.CascadingValue`1.Render(RenderTreeBuilder builder)
         at Microsoft.AspNetCore.Components.Rendering.ComponentState.RenderIntoBatch(RenderBatchBuilder batchBuilder, RenderFragment renderFragment)
         at Microsoft.AspNetCore.Components.RenderTree.Renderer.RenderInExistingBatch(RenderQueueEntry renderQueueEntry)
         at Microsoft.AspNetCore.Components.RenderTree.Renderer.ProcessRenderQueue()

dotnet: fail: Microsoft.AspNetCore.Components.Server.Circuits.CircuitHost[111]
      Unhandled exception in circuit '90nlVFG8p0qzJAYrk5UWbFjbpS0QSssA8Vq5p3liDws'.
      System.ArgumentNullException: Value cannot be null. (Parameter 'source')
         at System.Linq.ThrowHelper.ThrowArgumentNullException(ExceptionArgument argument)
         at System.Linq.Enumerable.Where[TSource](IEnumerable`1 source, Func`2 predicate)
         at BiaBuilder.Pages.Branch.<>c__DisplayClass0_1.<BuildRenderTree>b__11(RenderTreeBuilder __builder4) in C:\Users\Fellblade\Desktop\BIABuilder\BIA Builder\server\Pages\Branch.razor:line 44
         at Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder.AddContent(Int32 sequence, RenderFragment fragment)
         at Radzen.Blazor.RadzenDataGridRow`1.<>c__DisplayClass0_0.<BuildRenderTree>b__1(RenderTreeBuilder __builder2)
         at Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder.AddContent(Int32 sequence, RenderFragment fragment)
         at Radzen.Blazor.RadzenDataGridCell`1.<BuildRenderTree>b__0_2(RenderTreeBuilder __builder4)
         at Microsoft.AspNetCore.Components.CascadingValue`1.Render(RenderTreeBuilder builder)
         at Microsoft.AspNetCore.Components.Rendering.ComponentState.RenderIntoBatch(RenderBatchBuilder batchBuilder, RenderFragment renderFragment)
         at Microsoft.AspNetCore.Components.RenderTree.Renderer.RenderInExistingBatch(RenderQueueEntry renderQueueEntry)
         at Microsoft.AspNetCore.Components.RenderTree.Renderer.ProcessRenderQueue()

dotnet: info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
      Executed endpoint '/_blazor'

dotnet: info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
      Request finished HTTP/1.1 GET http://localhost:5000/_blazor?id=9zLpv2ikABPhV4jRg2aUVw - - - 101 - - 1327.3035ms

dotnet: warn: Microsoft.AspNetCore.Components.Server.Circuits.RemoteRenderer[100]
      Unhandled exception rendering component: Cannot access a disposed context instance. A common cause of this error is disposing a context instance that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling 'Dispose' on the context instance, or wrapping it in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.
      Object name: 'ApplicationIdentityDbContext'.
      System.ObjectDisposedException: Cannot access a disposed context instance. A common cause of this error is disposing a context instance that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling 'Dispose' on the context instance, or wrapping it in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.
      Object name: 'ApplicationIdentityDbContext'.
         at Microsoft.EntityFrameworkCore.DbContext.CheckDisposed()
         at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
         at Microsoft.EntityFrameworkCore.DbContext.get_Model()
         at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.get_EntityType()
         at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.CheckState()
         at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.get_EntityQueryable()
         at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.System.Linq.IQueryable.get_Provider()
         at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.AsNoTracking[TEntity](IQueryable`1 source)
         at BiaBuilder.SecurityService.GetUsers() in C:\Users\Fellblade\Desktop\BIABuilder\BIA Builder\server\Services\SecurityService.cs:line 176
         at BiaBuilder.Pages.BranchComponent.Load() in C:\Users\Fellblade\Desktop\BIABuilder\BIA Builder\server\Pages\Branch.razor.designer.cs:line 166
         at BiaBuilder.Pages.BranchComponent.OnInitializedAsync() in C:\Users\Fellblade\Desktop\BIABuilder\BIA Builder\server\Pages\Branch.razor.designer.cs:line 148
         at Microsoft.AspNetCore.Components.ComponentBase.RunInitAndSetParametersAsync()
         at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle)

dotnet: fail: Microsoft.AspNetCore.Components.Server.Circuits.CircuitHost[111]
      Unhandled exception in circuit '90nlVFG8p0qzJAYrk5UWbFjbpS0QSssA8Vq5p3liDws'.
      System.ObjectDisposedException: Cannot access a disposed context instance. A common cause of this error is disposing a context instance that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling 'Dispose' on the context instance, or wrapping it in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.
      Object name: 'ApplicationIdentityDbContext'.
         at Microsoft.EntityFrameworkCore.DbContext.CheckDisposed()
         at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
         at Microsoft.EntityFrameworkCore.DbContext.get_Model()

Page Designer lines producing the error:
line 166

            var securityGetUsersResult = await Security.GetUsers();
            users = securityGetUsersResult;

Line 148

     protected override async System.Threading.Tasks.Task OnInitializedAsync()
        {
            Globals.PropertyChanged += OnPropertyChanged;
            await Security.InitializeAsync(AuthenticationStateProvider);
            await Load();
        }

Page Razor Line 44

 <Template Context="data">
              @(users.Where( u => u.Id == data.UpdatedBy).FirstOrDefault().FirstName) @(users.Where( u => u.Id == data.UpdatedBy).FirstOrDefault().LastName)
              </Template>

Hey guys, i'm curretly trying to solve a very similar problem.
i have a Grid and i get data based on user roles. but the db call is happening before the page load so the first time the user is anonymous...
how can i solve that? should i delay ALL the grid calls? or is there a simple way to work with the generated grid?