The Wisej.NET Performance Model
This example is the WisejPerfLab support application with
a dashboard, a ticket grid and a customer tree over a seeded dataset of 50,000 tickets and 3,200
customer nodes, made measurable by a ScenarioProbe that wraps each named scenario in a Stopwatch
scope and writes structured PERF start / PERF end records.
Nothing in this module is optimised. That is the point: this is the before the whole course is
measured against, and every later module starts from the version in the previous folder. The three
deliverables are in docs/: Scenarios.md, Baseline.md and
Budget.md.
Run it
cd "LearnWisej-Samples/Performance and Profiling Course/Module 1/WisejPerfLab"
dotnet run -f net10.0 --urls http://localhost:5801
Measurements are taken in Release, and so are the numbers in docs/Baseline.md:
dotnet run -c Release -f net10.0 --urls http://localhost:5801
The first start creates and seeds WisejPerfLab/App_Data/perflab.db (about 60 MB, half a second) and
prints the row counts. Delete that folder to reseed; the seed is deterministic, so the rows come back
identical. The app header shows the dataset and the build configuration it is running under — if it
says Debug (measure in Release), the numbers on screen are not baseline material.
What to click
| Action | What you should see |
|---|---|
| Warm up (discarded run) | the three scenarios run once, bracketed in the PERF log by "warm-up run — the three records below are discarded" |
| Run the three scenarios ×3 | nine runs, then three median … lines and a status line with all three medians |
| Dashboard → Refresh | KPIs and the chart repaint; the chip says Refresh 476 ms — over the 250 ms budget rows=50,000 |
| Tickets → Search tickets | 5,000 rows, and the status line admits 5,001 SQL statements |
| Tickets → Redraw | the same rows rebuilt with no query at all — the allocation-only scenario Module 4 attacks |
| Tickets → Export CSV | the file name appears after about half a second; the progress bar never moves, because the click handler blocked on .Result and nothing reaches the browser until it returns |
| Tickets → double-click a row | the ticket detail form, showing bus subscribers: 1 timer: running — its two retention roots, on screen |
| Customers → Load the customer tree | 3,200 nodes and 3,201 SQL statements, built before you open anything |
| Customers → Expand the first branch | one branch opens, and another 3,201 statements are issued to do it |
| Break the database then any scenario | a red banner, a fault status, an emptied grid — and a PERF end … failed=DatabaseUnavailableException record with the time spent before the failure |
| Restore | the next run succeeds again |
| Clear log | the PERF buffer of this session empties |
Open a second browser tab to see that each session gets its own PERF log: the buffer lives in
Application.Session, not in a static field.
Where things live
WisejPerfLab/
Startup.cs host builder: SQLite, the probe, the services, the Wisej DI bridge, seeding
Program.cs Wisej session entry point (one MainPage per session)
MainPage.cs / .Designer.cs shell: three tabs, the PERF log card, warm-up / ×3 / break / restore
Diagnostics/
ScenarioProbe.cs Measure(scenario, userAction, rows) -> IDisposable scope ← the deliverable
PerfRecord.cs one PERF line
PerfLogBuffer.cs this session's records + MedianElapsed()
SessionPerfLog.cs finds the buffer in Application.Session
Shell/
PerfBudget.cs docs/Budget.md as code: threshold + tool per scenario
IPerfLabShell.cs status line and failure banner
IScenarioPage.cs a screen that owns one measured scenario
Pages/
DashboardPage.cs Dashboard/Refresh — load everything, format per row, rebuild the KPI panel
TicketGridPage.cs Tickets/Search, /Redraw, /Export
CustomerTreePage.cs Customers/LoadTree, /ExpandNode
Forms/TicketDetailForm.cs the detail form, with the leaks Module 4 will find
Services/ DashboardService, TicketSearchService, CustomerTreeService,
ExportService, TicketFormatter, GlobalTicketBus
Data/ PerfLabContext, entities, the deterministic seeder, the outage switch
docs/ Scenarios.md, Baseline.md, Budget.md
Known simplifications
- The database is SQLite in process, so statements are about six times cheaper than they would be
against SQL Server across a socket. The statement counts in this app are real; the wall-clock cost
of the N+1 is flattering.
docs/Budget.mdexplains what that did to the thresholds. - The chart is
Wisej-4-ChartJS. The lesson snippet writeschartLoad.DataSource = snapshot.ChartRows; the real control takesLabelsplusDataSets, so that is whatRebuildChartdoes. - The PERF records are also shown on screen. In production they would only go to the logging pipeline —
ScenarioProbewrites toILoggerfirst and to the session buffer second, and the console output ofdotnet runshows exactly the same lines.