---
title: Cross-application navigation
summary: Persists context and redirects/pops up to a target application or view, optionally starting a new model instance.
status: draft
owner: SAIF Documentation Team
last_reviewed: 2026-08-17
library: SAIF.Presentation.Web.MVC
reference_type: package
operation_id: OP-MVC-003
trigger: Explicit controller call (Navigate / Navigate2) from concrete application code
---

# Cross-application navigation

`Navigate`/`Navigate2` implement outbound navigation from the current page, either within the same SAIF application or across to a different one. Cross-application navigation persists in-memory context values to the shared `Context` table so the target application can retrieve them on arrival (see [Cross-application context hand-off](cross-application-context-hand-off.md)).

| Field | Value |
| --- | --- |
| **Operation ID** | OP-MVC-003 |
| **Trigger** | Explicit call to `clsControllerBase.Navigate(TargetApp, TargetView)` or the new-model overload `Navigate2(TargetApp, TargetView)` from a concrete controller |
| **Inputs** | Target application/view, popup flags, optional new-model GUID |
| **Outputs** | HTTP redirect or `window.open` client script; persisted context row(s) in [Context](../database-tables.md#context) |
| **Primary components** | `clsControllerBase.Navigate`, `clsControllerBase.Navigate2`, `clsContext.SaveContext` |
| **Entry point** | SAIF.Presentation.Web.MVC\clsControllerBase.vb |

## Steps

1. Compute `SourceApp` from the current `ApplicationPath`; build `NavTarget` (absolute URL, path-rooted, or `/{TargetApp}/{TargetView}`).
2. Stash `targetView`/`targetApp`/`sourceApp`/`sourceView` into `Model.Context` (in-memory, not yet persisted).
3. If `SourceApp <> TargetApp` (cross-application navigation) ([BR-MVC-005](../business-rules.md#br-mvc-005-cross-application-context-transfer-only-across-distinct-applications)):
   - If `SupportsMultiModel`, also stash the current `ViewID` under `__SAIFPID`.
   - Unless the session has timed out, call `Model.Context.SaveContext(SessionID)`:
     - Executes [SQL-MVC-004](../sql/SQL-MVC-004-delete-context-rows.md) (`DELETE FROM Context WHERE AppName=@AppName AND SessionID=@SessionID`) against [Context](../database-tables.md#context).
     - Executes [SQL-MVC-001](../sql/SQL-MVC-001-insert-context-value.md) once per stored context key (an `INSERT INTO Context` statement supplying the application name, session ID, context key, and context value) against [Context](../database-tables.md#context).
4. If `SourceApp = TargetApp` (same-application navigation), store the (possibly new) `ViewID`/model GUID into `Session[__SAIFPID]` instead of persisting to the database.
5. If `Popup` is true, register a client startup script that calls `window.open` with the computed target URL, window name, and popup features, plus an optional focus-blur script.
   - Otherwise: `Response.Redirect(NavTarget)` and `Response.End()`.
   - On failure: `ThreadAbortException` is swallowed via `Thread.ResetAbort()`.
6. `Navigate2` additionally, before delegating to `Navigate`:
   - Requires `SupportsMultiModel` ([BR-MVC-004](../business-rules.md#br-mvc-004-multi-model-requires-configuration)); throws `ApplicationException("Navigate2 only supported with Multiple Model configuration.")` otherwise.
   - Creates a brand-new `clsModelBase` via the factory, assigns a fresh `clsSecurityPrincipal`, runs `Pre_Init()`.
   - Invokes the overridable `OnNewModel(newModel, newViewData)` hook to let the concrete controller copy state from the old model to the new one.
   - On success, copies existing `ViewData` entries into the new view-data dictionary, stores the new model/view-data in `Session` under a new GUID, then delegates to `Navigate(inTargetApplication, inTargetView, inNewModel:=NewGuid)`.

## Process flow

```mermaid
flowchart TD
	Start([Navigate called]) --> Compute[Compute SourceApp / NavTarget]
	Compute --> StashCtx[Stash targetView/targetApp/sourceApp/sourceView in Model.Context]
	StashCtx --> CrossApp{SourceApp <> TargetApp?}
	CrossApp -->|Yes| SaveCtx[SaveContext: delete then insert rows]
	CrossApp -->|No| SessionStore[Store ViewID in Session]
	SaveCtx --> PopupCheck{Popup?}
	SessionStore --> PopupCheck
	PopupCheck -->|Yes| Script([Register window.open startup script])
	PopupCheck -->|No| Redirect([Response.Redirect + Response.End])
```

## Failure and edge-case behavior
- `Navigate2` throws `ApplicationException` immediately if `SupportsMultiModel` is `False` — no partial state is created.
- `SaveContext`'s delete-then-insert has no rowcount/contention validation; a concurrent `SaveContext` call for the same application/session could interleave.
- `ThreadAbortException` from `Response.Redirect`/`Response.End` is caught and suppressed in both `Navigate` and `HandleErrors`.

## Related documentation
- [Operations and processes](../operations-processes.md)
- [Business rules](../business-rules.md)
- [SQL catalog](../sql/index.md)
- [Database tables](../database-tables.md)

## Sources
- SAIF.Presentation.Web.MVC\clsControllerBase.vb
- SAIF.Presentation.Web.MVC\clsContext.vb
