---
title: Commit
summary: Commits the active transaction and clears the session.
status: draft
owner: Framework
last_reviewed: 2026-10-02
library: SAIF.NetCore.NHibernate
reference_type: package
operation_id: OP-NH-08
trigger: Consumer calls IUnitOfWork.Commit()
---

# Commit

This operation commits the transaction opened by the most recent [Begin transaction](begin-transaction.md), then calls `Session.Clear()` to clear the session's first-level cache/tracking. `Clear()` is not a flush: whether pending changes were actually synchronized to the database by this point depends on the session's `FlushMode`, which this library does not set.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-NH-08 |
| **Trigger** | Consumer calls `IUnitOfWork.Commit()` |
| **Inputs** | — |
| **Outputs** | Committed transaction; cleared session, or a thrown exception |
| **Primary components** | `UnitOfWork.Commit` |
| **Entry point** | `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/UnitOfWork.cs` |

## Steps

1. Read `Session.Transaction()`.
2. If `transaction.IsActive` is `false`: throw `InvalidOperationException("You must call Begin() on the unit of work before you can commit.")` ([BR-NH-13](../business-rules.md#br-nh-13-commit-requires-an-active-transaction))
3. If active: call `transaction.Commit()`.
4. Call `Session.Clear()` after a successful commit. On the **stateful** strategy this discards the first-level cache so subsequent reads on the same session hit the database again. On the **stateless** strategy, `Clear()` is a literal no-op — a stateless session has no first-level cache to discard, and its `Add`/`Update`/`Delete` calls already write immediately rather than queuing changes for a flush-at-commit, so `Commit()` there is effectively a COMMIT-only operation with no equivalent cache-clearing effect.

`Commit` contains no literal SQL text in this library's own source, but that does not mean no database activity occurs: `transaction.Commit()` is a call into NHibernate's `ITransaction`, which internally issues a database `COMMIT` and, for a **stateful** session, triggers NHibernate's auto-flush of any pending INSERT/UPDATE/DELETE changes before committing — subject to the session's own externally-configured `FlushMode` (for example `FlushMode.Never`/`Manual` would suppress the implicit flush); this is not an unconditional guarantee independent of session configuration. For a **stateless** session there is no such flush step, because stateless `Add`/`Update`/`Delete` operations already execute immediately against the database rather than being queued. This is NHibernate/ADO.NET provider behavior external to this library's own source — well-known NHibernate transaction semantics, but not something this repository's source directly confirms since it doesn't ship NHibernate's own source.* `Commit` is also the final step of [Test connection](test-connection.md)'s `Begin`/query/`Commit` cycle, which does execute [SQL-004](../sql/SQL-004-test-connection.md).

## Process flow

The diagram below visualizes the active-transaction check, the commit/session-clear path, and the thrown-exception path when no transaction is active.

```mermaid
flowchart TD
	Start(["Commit() called"]) --> Check{"transaction.IsActive?"}
	Check -->|No| Throw(["Throw InvalidOperationException"])
	Check -->|Yes| DoCommit["transaction.Commit()"]
	DoCommit --> ClearSession["Session.Clear()<br>(stateful: discards cache; stateless: no-op)"]
	ClearSession --> Return(["Transaction committed"])
```

## Failure and edge-case behavior
- Calling `Commit()` without a preceding successful `Begin()`, or after the transaction has already been committed or rolled back, throws `InvalidOperationException` with message `"You must call Begin() on the unit of work before you can commit."`.
- `Commit()` does not dispose the session. Against NHibernate 5.2.7, a successful commit detaches the completed transaction from the session (`ConnectionManager.AfterTransaction()` sets `_transaction = null`), so a later [Begin transaction](begin-transaction.md) on the same unit of work starts a new transaction and does not throw. A second `Commit()` without a new `Begin()` throws, as described above, because the session's current transaction is then a new, inactive one.
- `Session.Clear()` after commit means entities loaded before the commit become detached; reusing them without reattachment can raise lazy-loading exceptions. This applies to the **stateful** strategy only — the stateless strategy's `Clear()` is a no-op, and stateless sessions never hold first-level-cached entities to detach in the first place.*

## Related documentation
- [Operations and processes](../operations-processes.md)
- [Business rules](../business-rules.md)
- [Begin transaction](begin-transaction.md)
- [Rollback](rollback.md)

> **Note:** Statements marked with `*` were inferred from code structure, naming
> conventions, or contextual evidence where direct confirmation was not available
> in source artifacts.

## Sources
- `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/UnitOfWork.cs`
- NHibernate 5.2.7 (resolved via FluentNHibernate 3.0.1): `src/NHibernate/Transaction/AdoTransaction.cs` (`Commit`, `AfterTransactionCompletion`), `src/NHibernate/AdoNet/ConnectionManager.cs` (`AfterTransaction`, `Transaction`)
