For some reason when bringing up the page CampaignPage.razor, OnInitializedAsync() is being called twice.
In addition, the property ReturnUrl is null each time (should be the first time) and is null again when HandleValidSubmitAsync() is called.
The first time it hits it, this is the call stack:
LouisHowe.web.dll!LouisHowe.web.Pages.Manager.CampaignPage.OnInitializedAsync() Line 143 C# [External Code] LouisHowe.web.dll!LouisHowe.web.Pages.Pages__Host.ExecuteAsync.AnonymousMethod__23_1() Line 40 C# [External Code] And the second time, this is it:
LouisHowe.web.dll!LouisHowe.web.Pages.Manager.CampaignPage.OnInitializedAsync() Line 143 C# [External Code] In addition it's 5+ seconds between the two hits and there's very little the system is doing to initialize the page. It hits the database but that's Sql Server Express sitting on my computer doing nothing else.
This only happens for case of mode == read or update. And in that case, in OnInitializedAsync() it is calling the following code. So it's something in this code that is causing the issue.
Note that it calls the same dbContext to get Countries.All() for the case of mode == create and that does not have the problem. So it's something in the call to Campaigns.FindByNames() that's doing it - but how?
await using (var dbContext = await NoTrackingDbFactory.CreateDbContextAsync()){ // 1. if no uniqueId then create mode (because what would we Read/Update)? if (string.IsNullOrEmpty(UniqueId)) ModeState = Mode.Create; else { // 2. if have a uniqueId then it can't be create. if (ModeState == Mode.Create) ModeState = Mode.Read; // 3. Read it in from the database. _campaign = await dbContext.Campaigns.FindByNames(string.Empty, UniqueId, CampaignIncludes.All).FirstOrDefaultAsync(); if (_campaign == null) { _badUniqueId = true; ModeState = Mode.Read; return; } Model.Campaign = _campaign; } var list = new List<IOrganization>(); list.AddRange(await dbContext.Countries.All().ToListAsync()); list.Add(Country.Dashes); list.AddRange(await dbContext.States.All().ToListAsync()); AllRegions = list;}And here's FindByNames
public static IQueryable<Campaign> FindByNames(this IQueryable<Campaign> source, string searchName, string searchUniqueId, params Expression<Func<Campaign, object?>>[] includes){ var query = source; if (searchName.Length != 0) query = query.Where(campaign => campaign.Name.Contains(searchName)); if (searchUniqueId.Length != 0) query = query.Where(campaign => campaign.UniqueId.Contains(searchUniqueId)); foreach (var include in includes) query = query.Include(include); return query.OrderBy(i => i.Name);}So, what would cause the long delay and the multiple calls?
So I did as @MrCakaShaunCurtis suggested and put a debug entering and leaving OnInitializedAsync() and here's what I get.
Only once case (mode == create):
enter OnInitializedAsync() CampaignPage Instance a69f0a2d-e36a-4297-b8a1-d8d9cd12bbd1... 2 expected DB callsleave OnInitializedAsync() CampaignPage Instance a69f0a2d-e36a-4297-b8a1-d8d9cd12bbd1enter OnAfterRenderAsync() CampaignPage Instance a69f0a2d-e36a-4297-b8a1-d8d9cd12bbd1leave OnAfterRenderAsync() CampaignPage Instance a69f0a2d-e36a-4297-b8a1-d8d9cd12bbd1enter OnAfterRenderAsync() CampaignPage Instance a69f0a2d-e36a-4297-b8a1-d8d9cd12bbd1leave OnAfterRenderAsync() CampaignPage Instance a69f0a2d-e36a-4297-b8a1-d8d9cd12bbd1And for the calls twice case:
enter OnInitializedAsync() CampaignPage Instance 427cd0b9-bf07-41d7-924f-0355da16b441... 4 (FindByNames query becomes 2 calls) expected DB callsleave OnInitializedAsync() CampaignPage Instance 427cd0b9-bf07-41d7-924f-0355da16b441Microsoft.EntityFrameworkCore.Database.Command: Information: Executed DbCommand (2ms) [Parameters=[@__userid_0='?' (Size = 450)], CommandType='Text', CommandTimeout='30']SELECT TOP(1) [a].[Id], [a].[AccessFailedCount], [a].[ConcurrencyStamp], [a].[Email], [a].[EmailConfirmed], [a].[LockoutEnabled], [a].[LockoutEnd], [a].[NormalizedEmail], [a].[NormalizedUserName], [a].[PasswordHash], [a].[PhoneNumber], [a].[PhoneNumberConfirmed], [a].[SecurityStamp], [a].[TwoFactorEnabled], [a].[UserName]FROM [AspNetUsers] AS [a]WHERE [a].[Id] = @__userid_0enter OnInitializedAsync() CampaignPage Instance 2b5a7537-85cc-4bca-9b29-403f8cdba668... 4 calls againleave OnInitializedAsync() CampaignPage Instance 2b5a7537-85cc-4bca-9b29-403f8cdba668'LouisHowe.web.exe' (CoreCLR: clrhost): Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\7.0.5\System.Web.HttpUtility.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.enter OnAfterRenderAsync() CampaignPage Instance 2b5a7537-85cc-4bca-9b29-403f8cdba668leave OnAfterRenderAsync() CampaignPage Instance 2b5a7537-85cc-4bca-9b29-403f8cdba668'LouisHowe.web.exe' (CoreCLR: clrhost): Loaded 'C:\git\LouisHowe\LouisHowe.web\bin\Debug\net7.0\Newtonsoft.Json.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.enter OnAfterRenderAsync() CampaignPage Instance 2b5a7537-85cc-4bca-9b29-403f8cdba668leave OnAfterRenderAsync() CampaignPage Instance 2b5a7537-85cc-4bca-9b29-403f8cdba668enter OnAfterRenderAsync() CampaignPage Instance 2b5a7537-85cc-4bca-9b29-403f8cdba668leave OnAfterRenderAsync() CampaignPage Instance 2b5a7537-85cc-4bca-9b29-403f8cdba668So it's calling the Identity DB to get the AspNetUsers user record. It generally does that before each call to OnInitializedAsync(), I assume to initialize the value in the Task<AuthenticationState> cascading parameter.
So it's re-initializing, before ever getting to OnAfterRenderAsync().
I made the change @MarvinKlein suggested (2 places) - that fixed it. I now have:
@page "/"@using Microsoft.AspNetCore.Components.Web@using Microsoft.AspNetCore.Mvc.TagHelpers@using LouisHowe.web@namespace LouisHowe.web.Pages@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Xsrf<!DOCTYPE html><html lang="en"><head><!-- Copyright (c) 2023 by David Thielen - ALL RIGHTS RESERVED --><meta charset="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1.0"/><base href="~/"/><link rel="stylesheet" href="css/bootstrap/bootstrap.min.css"/><link href="_content/DevExpress.Blazor.Themes/blazing-berry.bs5.min.css" rel="stylesheet" asp-append-version="true"/><link href="css/site.css" rel="stylesheet" asp-append-version="true"/><link href="css/menu.css" rel="stylesheet" asp-append-version="true"/><link href="css/header.css" rel="stylesheet" asp-append-version="true"/><link rel="stylesheet" href="css/dx-demo.css" asp-append-version="true"><link rel="stylesheet" href="css/dx-demo-pages.css" asp-append-version="true"><link href="LouisHowe.web.styles.css" rel="stylesheet"/><link rel="icon" type="image/png" href="favicon.png"/><script src="~/js/Interop.js"></script><component type="typeof(HeadOutlet)" render-mode="Server"/></head><body> @{ var initialTokenState = new Services.InitialApplicationState { XsrfToken = Xsrf.GetAndStoreTokens(HttpContext).RequestToken, Cookie = HttpContext.Request.Cookies[".AspNetCore.Cookies"] }; }<component type="typeof(App)" render-mode="Server" param-InitialState="initialTokenState" /><div id="blazor-error-ui"><environment include="Staging,Production"> An error has occurred. This application may no longer respond until reloaded.</environment><environment include="Development"> An unhandled exception has occurred. See browser dev tools for details.</environment><a href="" class="reload">Reload</a><a class="dismiss">🗙</a></div><script src="_framework/blazor.server.js"></script></body></html>And this is explained here. Which leads to another question that I'll post separately.