Who Owns the UI
This example is the Session Inspector +
Lifecycle Logger of TicketOps Live. A diagnostics group box (diagnosticsGroupBox) shows the current time, the
client id, the session id, the browser, the server thread serving the current event and a per-session counter kept
in Application.Session; a lifecycle log (lifecycleListBox) records load, the counter, the background update,
other sessions joining and leaving (through a global SessionRegistry), the session timeout and the exit.
Run it
cd "LearnWisej-Samples/Real-Time Server Push Course/Module 2/TicketOpsLive"
dotnet run -f net10.0 --urls http://localhost:5302
Then open http://localhost:5302, and open it a second time (or click Open second window).
What to try
| Control | What you should see |
|---|---|
| Counter +1 | counterLabel grows; a second tab keeps its own value |
| Background update | ~1.5 s later "Background update at …" appears in the log with no click in between; the server thread id changes |
| Open second window | a second tab: same client id (it identifies the browser), its own session id and counter; both logs show Session xxxxxxxx joined |
| Clear log | empties the lifecycle log |
| Close or refresh the other tab | once its session ends, this tab logs Session xxxxxxxx left without anyone clicking here |
Server thread. threadLabel is refreshed on every event: the id moves because the server thread is a pool
thread reused for whichever session sends the next event. Out-of-bound code needs the context, not the thread.
After a refresh (F5), and after the session ends
| What you look at | After F5 | Why |
|---|---|---|
counterLabel (Application.Session.Counter) | back to 0 | the bag belonged to the session that ended |
| lifecycle log | empty | control state of a page that no longer exists |
clientIdLabel / sessionIdLabel | same client id, new session id | a new session in the same browser |
liveSessionsLabel | the old id leaves, the new one joins | the global registry outlives sessions |
When a session ends (Application.Exit() or the timeout), ApplicationExit logs TicketOps session exited: <ClientId>
to the server console and Cleanup() unsubscribes SessionsChanged, unregisters the session and detaches
ApplicationExit/SessionTimeout — once, even though Disposed calls it too. About two minutes before the idle timeout,
Application.SessionTimeout logs "Session is about to time out" and Wisej.NET shows its "prolong the session?" dialog.
Polling fallback. Add "enableWebSocket": false to Default.json and restart: the background update still
arrives (about a second late) because polling is requested while the task runs.