Skip to main content

Database, File I/O, Async and Waits

The two wait-heavy paths are rewritten:

  • One statement per page. The N+1 behind the grid is gone: TicketQueryService issues a single AsNoTracking projection with the customer name joined and only the displayed columns selected — 201 statements → 1, and Tickets/Search 125 ms → 66 ms. The filter and sort columns are now indexed.
  • The export never blocks. Application.StartTask replaces .Result, one StreamWriter replaces 5,001 file opens, and progress is reported every ten percent — 522 ms → 171 ms, with the request thread free and the screen usable throughout.

Evidence: QueryTrace.md · ExportRework.md

Run it

cd "LearnWisej-Samples/Performance and Profiling Course/Module 6/WisejPerfLab"
dotnet run -c Release -f net10.0 --urls http://localhost:5806

Module 5 on 5805 is the before.

What to click

ActionWhat you should see
Tickets → Search tickets66 ms — within the 300 ms budget, then 1 page(s) held 1 fetch(es) 1 statements — one statement for the page, where Module 5 needed 201
Tickets → Export CSVthe button greys out, Cancel lights up, the progress bar moves in ten steps, then exported tickets-….csv in 171 ms
click something else while the export runsit works — the request thread was never held
Cancel during an exportexport cancelled after … ms — the partial file was left on disk
Break the databaseExport CSVthe button comes back, the banner says why, and the PERF end record is written with the failure
Export CSV twice quicklythe second click is dropped, and the status line says so
Rows 50000Search ticketsExport CSVthe whole open population exported, still without blocking

What changed since Module 5

Services/TicketQueryService.cs one AsNoTracking projection query: joined, narrowed, ordered, paged
+ SearchAsync for callers that can await
Services/ExportService.cs rewritten: takes TicketQueryService, one StreamWriter, ten progress
reports, a CancellationToken
Pages/TicketGridPage.cs the export runs in Application.StartTask; + Cancel; + IsDisposed checks
Pages/TicketGridPage.Designer.cs + btnCancelExport, the progress bar moved under the grid
Data/PerfLabContext.cs + IX_Tickets_Status_UpdatedAt
Data/PerfLabDatabase.cs + CREATE INDEX IF NOT EXISTS (EnsureCreated cannot add it to an
existing file; a real app would add a migration)

Known simplifications

  • SQLite runs in process, so the 201-statement N+1 cost far less here than it would against a database server. The statement count is real; the milliseconds are flattering.
  • Concurrent exports from several sessions were not tested — see ExportRework.md.