Rollback¶
This operation reverses the unit of work's transaction and — unlike Commit — tears down the session afterward on the normal (non-throwing) path, since there is no try/finally guarding the disposal call.
| Field | Value |
|---|---|
| Operation ID | OP-NH-09 |
| Trigger | Consumer calls IUnitOfWork.Rollback() |
| Inputs | — |
| Outputs | Transaction rolled back if it was active; session disposed only when transaction.Rollback() itself does not throw |
| Primary components | UnitOfWork.Rollback |
| Entry point | src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/UnitOfWork.cs |
Steps¶
- Read
Session.Transaction(). - If
transaction.IsActiveistrue: calltransaction.Rollback(). - If
transaction.IsActiveisfalse: do nothing here — unlike Commit,Rollbackdoes not throw when no transaction is active. - If
transaction.Rollback()did not throw, callSession.Dispose()next. There is notry/finallyaround the rollback call: iftransaction.Rollback()itself throws,Session.Dispose()is skipped entirely and the exception propagates to the caller —Rollback()provides no exception-safety guarantee that cleanup always runs (BR-NH-14). Rollback()does not set theisDisposedflag — onlyUnitOfWork.Dispose()/Dispose(bool)does that. CallingRollback()and laterDispose()still runs the full disposal logic in Dispose unit of work; disposal is not skipped just becauseRollback()ran first.
Process flow¶
The diagram below visualizes the active-transaction check, the rollback-throws vs. rollback-succeeds paths, and the session-disposal step that only the non-throwing path reaches.
flowchart TD
Start(["Rollback() called"]) --> Check{"transaction.IsActive?"}
Check -->|Yes| DoRollback["transaction.Rollback()"]
Check -->|No| Skip["No rollback performed<br>(no exception thrown)"]
DoRollback -->|"throws"| Propagate(["Exception propagates;<br>Session.Dispose() skipped"])
DoRollback -->|"succeeds"| DisposeSession["Session.Dispose()"]
Skip --> DisposeSession
DisposeSession --> Return(["Session disposed on this path"])
Failure and edge-case behavior¶
- Calling
Rollback()with no active transaction does not throw — it still proceeds to dispose the session (per step 3). This is asymmetric with Commit, which throws in that same situation.* - When
transaction.Rollback()succeeds (or no rollback was needed),Session.Dispose()runs. Whether a subsequent call on the unit of work fails is not guaranteed by this page: the stateful session strategy'sSessiongetter can transparently reopen a closed-but-not-fully-disposed session (see ISessionStrategy), so "the session is now closed" does not necessarily mean every further call fails — the exact behavior of further calls after disposal depends on strategy and is not fully characterized here.** - When
transaction.Rollback()throws,Session.Dispose()is skipped (no try/finally) and the exception propagates before any disposal occurs. Whether the session is consequently left in a reusable or broken state is not confirmed by this library's own source — this library does not prove either outcome.** Rollback()does not setisDisposed, so calling Dispose unit of work afterRollback()still runs its full disposal logic rather than being skipped — see that page for the exact flag-order andIsNull()-side-effect caveats.
Related documentation¶
Note: Statements marked with
*were inferred from code structure, naming conventions, or contextual evidence where direct confirmation was not available in source artifacts. Statements marked with**could not be confirmed; treat as unknown until verified.
Sources¶
src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/UnitOfWork.cs