Skip to main content

Startup, Configuration, Session State & Lifetime

The per-user values the TicketOps Console kept reaching for (operator, tenant, theme, client profile, the selected ticket) move out of static fields into a session-scoped SessionContext that is created once per browser session by AppComposition and constructor-injected into the Form and every service. A Session diagnostics page shows, side by side, what is shared by the whole app (configuration read once per process, the shared ticket store, a process counter) and what belongs to just this session. A static-state audit is applied to the project (docs/StaticStateAudit.md).

Nothing here is deployed anywhere; it is a plain Wisej.NET 4 project that runs on your machine.

Run it

cd "LearnWisej-Samples/Production Architecture Deep Dive\Module 2\TicketOps"
dotnet run -f net10.0 --urls http://localhost:5102

Then open http://localhost:5102. (Visual Studio: open TicketOps.slnx, press F5 — the port is in Properties/launchSettings.json.)

Requirements: .NET 10 SDK, the Wisej-4 4.1.0 NuGet package.

What to try in the Session diagnostics window

The Application panel (blue) is identical in every session; the This session panel (green) comes from the injected SessionContext. Below them: the per-session selectors and the open tickets of this session's tenant.

ActionWhat you should see
change operator / tenant / theme, Apply to this sessionthe green panel and the window caption update; the grid reloads for the tenant; status ● Signed in as Bob Chen · Contoso. A theme restyles this tab only
pick a tenant the operator does not belong to (e.g. Bob Chen → Fabrikam), Applyorange banner Bob Chen is not a member of Fabrikam.; the tenant does not change
select a ticket rowTicket 1003 selected for this session.; green panel Selected ticket = #1003
both panels re-read (use it in the other tab during the two-tab test)

The two-tab test (lab step 9: "run two sessions and confirm they don't leak state into each other")

Open http://localhost:5102 in a second browser tab. Each tab is its own Wisej.NET session — like a second launch of a desktop app. Call them A and B.

StepIn tab AThen in tab BExpected
1read Session idread Session iddifferent ids; Active sessions = 2 in both after
2operator → Bob Chen, Apply to this sessionpress A: User = Bob Chen. B: User = Alice Riverano leak
3operator Alice Rivera, tenant Fabrikam, Applypress A's grid shows the 3 Fabrikam tickets. B still shows Contoso — no leak
4theme → Material-3, Applylookonly A restyles — no leak (per-session Application.Theme, not the global LoadTheme)
5click ticket 2001press A: Selected ticket = #2001. B: (none)no leak
6compare the blue panelsidentical: the shared, read-only settings both tabs must see
7press F5 in AWisej.NET keeps the session: the same Session id

Where things live

TicketOps/
├─ Views/SessionDiagnostics the screen: injected SessionContext; thin handlers; ShowResult / ReportFailure
├─ Controls/StatusBanner reusable "● state" + banner UserControl (display only)
├─ Services/
│ ├─ SessionContext.cs ONE PER SESSION: session id, user, tenant, theme, client profile, selected ticket
│ ├─ ISessionService / SessionService.cs sign-in, tenant switch (membership rule), theme choice, ticket selection
│ └─ ITicketService / TicketService.cs tenant-filtered ticket list
├─ Domain/ UserAccount (IsMemberOf rule), Ticket, OperationResult
├─ Data/
│ ├─ SharedTicketStore.cs PROCESS-WIDE, thread-safe ticket store (stands in for the database)
│ ├─ InMemoryTicketRepository.cs per-session door to the shared store
│ └─ IUserDirectory / InMemoryUserDirectory.cs fake user store
├─ Infrastructure/
│ ├─ AppComposition.cs who gets what: one object graph per session; where SessionContext is born
│ ├─ ProcessScope.cs the "once per process" moment: Lazy<AppSettings>
│ ├─ AppSettings.cs appsettings.json → immutable object (the deployable contract)
│ ├─ SharedCounters.cs the one mutable shared value (Interlocked)
│ ├─ WisejRuntimeInfo.cs IRuntimeInfo over Application.* (SessionCount, Configuration, Theme, ActiveProfile)
│ ├─ ThemeCatalog.cs builds a per-session ClientTheme from the framework's embedded theme JSON
│ └─ ILog.cs / ActivityLog.cs logging (one log per session, written to the server console)
├─ Diagnostics/ DiagnosticsService, DiagnosticsSnapshot, IDiagnosticsService, IRuntimeInfo
├─ Resources/Strings.cs safe user-facing messages
├─ docs/ StaticStateAudit.md · ProductionReadinessNote.md · ConfigurationContract.md
├─ appsettings.json the application-wide settings (read once per process)
├─ Default.json Wisej.NET config: startup, theme, sessionTimeout (1200 s)
├─ Program.cs Wisej.NET session entry point → AppComposition (once per session)
└─ Startup.cs Kestrel host (app.UseWisej()) — once per process