Lab reference
Shared shell
MainLayout.razor renders the shell immediately. Its first interactive render loads preferences through UiPreferences; subsequent theme and drawer-button events await their writes. Only isDarkMode and drawerOpen are persisted; a responsive drawer that closes itself on a small screen is not saved as a preference. CSS determines the small-screen theme control. If storage or the interop script fails, preferences stay in memory and the shell keeps working.
Custom colors use MudBlazor palette variables. The shell includes a skip link, semantic main landmark, labeled icon controls, error recovery, and navigation to every lab. The skip link keeps the current path (a bare #main-content would resolve against <base href> and leave the page) and moves focus to the main landmark. Internal links are base-relative (labs, not /labs), so the same markup works at / and under the GitHub Pages demo path. Reload lab forces a full reload, because a link to the current address is an in-app navigation that keeps a failed error boundary.
LabFrame.razor reads LabCatalog, displays objectives, linked prerequisites, host notes and exercises, and wraps the experiment in an error boundary. Recovery invokes the experiment’s reset callback before recreating child content. Labs without a safe reset callback offer a full reload.
State and forms
Counter.razorowns the component count;CounterControl.razorraises an event callback.ScopedCounteris a scoped service: one per interactive circuit in Blazor Server, one per browser tab in WebAssembly. Its lifetime differs from prerendered component state.WeatherForecastdefines validation, sensible defaults, a draft copy, and validated application of changes.ForecastFields.razorconnects each input to the EditForm withForexpressions.EditWeather.razoredits a copy. The caller applies it only after a successful dialog result.
DataGrid
/weather uses a component-owned list and seeded generator. Generate dataset applies the chosen size and seed; Reset experiment also restores the defaults (100 rows, seed 42). Both recreate the grid, clearing its search, filters, sort, selection, and pagination. The date column binds the date value and uses a display format.
Search is debounced. Copy is available from visible row/toolbar controls; the row context menu is an additional shortcut whose anchor is inert, so it adds no empty focusable control. Selected CSV copy is limited to 1,000 rows. Dataset generation timings measure generation only, not browser rendering or concurrent-server capacity.
HTTP API
GET /api/forecasts accepts the parameters below. ForecastQuery holds the shared validation rules.
| Parameter | Default | Bound |
|---|---|---|
count |
1000 | 1–69,420 |
page |
0 | 0–69,420 |
pageSize |
25 | 1–100 |
search |
empty | 120 characters |
delayMs |
0 | 0–2000 |
descending |
false | Boolean, sort by date |
fail |
false | Boolean, simulate HTTP 503 |
culture |
en-US |
en-US or pt-BR; how search matches dates and numbers |
Responses contain { items, total }. Invalid bounds return a 400 validation problem (application/problem+json) whose errors are keyed by parameter. The API cannot see the caller’s UI culture, so the typed client sends culture; dates also match as yyyy-MM-dd. The endpoint observes request cancellation. The typed client uses a configured internal base URL. In the API page, Load page, Previous, and Next start a new load, cancel the previous request, and ignore its late result. Previous and Next page the query that produced the visible results, not fields edited since. Editing fields alone does not start or cancel a request; press Load page to apply the changed inputs. Cancel request, reset, and disposal also cancel owned work.
SQLite notebook
The page depends on INotebookStore. It cancels its own work on disposal, ignores an older load that finishes after a newer one, and receives the prerendered list through [PersistentState], so a page load reads the notebook once.
NotebookService creates a DbContext per operation. All queries and writes include the current workspace. The workspace cookie is issued only for page renders (not static files, APIs or health probes) and re-issued on each visit, so its 30-day lifetime slides. The root passes the protected-cookie workspace into the circuit as a server component parameter; interactive services do not depend on a live HttpContext. A GUID version token implements optimistic concurrency on SQLite; EF Core’s concurrency exception becomes the shared NotebookConflictException. A workspace holds at most 50 notes. Conflicts retain the UI draft and require reloading the current note rather than silently overwriting another tab’s change.
Migrations run at startup for this single-instance learning app. Notebook reset deletes only the current workspace’s notes and requires explicit confirmation in the page.
Identity, culture, files, and diagnostics
POST /auth/demoaccepts onlystudentorinstructor; it is disabled unless the demo feature is enabled.POST /auth/logoutclears the cookie. Both require antiforgery validation.GET /api/instructorenforces an authenticated instructor policy on the server.POST /culturevalidates an antiforgery token and a supported culture (en-USorpt-BR), writes a culture cookie, and starts a new page/circuit.ForecastCsvhandles quoted commas, quotes, CRLF, and multiline fields. Imports are at most 1 MiB/1,000 rows and reject invalid or duplicate identities before returning a dataset. Export neutralizes formula-like summaries (and a leading apostrophe) with an apostrophe; import removes exactly one, so a round trip keeps every summary unchanged, even at the 120-character limit. Blank lines are ignored.BatchExperimentruns bounded, cancellable asynchronous work. The page cancels it on reset or disposal, and its live region announces state changes rather than every progress step.GET /api/diagnosticslogs a trace ID and returns it./healthzis liveness;/healthz/readyqueries SQLite.
WebAssembly demo stand-ins
The static demo renders the same pages with browser implementations from Features/StaticDemo:
| Lab | Stand-in | What still holds | What only the server shows |
|---|---|---|---|
| API | ForecastApiSimulator, an HttpMessageHandler behind the same typed client |
validation, latency, 503 failure, cancellation, paging | real HTTP, network payloads |
| Notebook | BrowserNotebookStore on localStorage |
drafts, version conflicts between tabs, the note limit | SQLite, EF Core, migrations, workspace cookie |
| Authentication | DemoAuthenticationStateProvider |
AuthorizeView and policies in the UI |
authorization: 401/403/200 from an endpoint |
| Localization | culture in localStorage, applied before the runtime starts | resource strings, formatting, lang |
request localization and the culture cookie |
| Observability | browser console logging | structured log with a trace ID | health checks, server traces |
Each lab shows a host note explaining the difference.