---
title: Begin transaction
summary: Starts a ReadCommitted-isolation transaction on the unit of work's session.
status: draft
owner: Framework
last_reviewed: 2026-10-02
library: SAIF.NetCore.NHibernate
reference_type: package
operation_id: OP-NH-07
trigger: Consumer calls IUnitOfWork.Begin()
---

# Begin transaction

This operation starts a new transaction on the unit of work's session. It refuses only while another transaction on that session is still active. On a **stateful** session it first clears the session so in-memory state starts in sync with the database; on a **stateless** session, clearing is a no-op (there is no first-level cache to discard).

| Field | Value |
| --- | --- |
| **Operation ID** | OP-NH-07 |
| **Trigger** | Consumer calls `IUnitOfWork.Begin()` |
| **Inputs** | — |
| **Outputs** | An active `ReadCommitted` transaction on the unit of work's session, or a thrown exception |
| **Primary components** | `UnitOfWork.Begin`, `IsTransactionInProgress` |
| **Entry point** | `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/UnitOfWork.cs` |

## Steps

1. `IsTransactionInProgress()` reads the current transaction via `Session.Transaction()` and returns `transaction.IsActive || transaction.WasCommitted || transaction.WasRolledBack`. Against NHibernate 5.2.7, `Session.Transaction` never returns `null`. After a commit or rollback it returns a new, un-begun transaction, so in practice this check is `true` only while a transaction is active.
   - If `true`: throw `InvalidOperationException("There is a transaction already in progress.")` ([BR-NH-12](../business-rules.md#br-nh-12-begin-refused-while-a-transaction-is-active)).
2. If no transaction is in progress: call `Session.Clear()`. On the **stateful** strategy this discards any uncommitted changes held in NHibernate's first-level cache, keeping the session and database in sync. On the **stateless** strategy, `Clear()` is a literal no-op (`{ }` — there is no first-level cache for a stateless session to discard).
3. Call `Session.BeginTransaction(IsolationLevel.ReadCommitted)` through `UnitOfWork.Begin()`. This entry point always passes `ReadCommitted`, and the **stateful** strategy's `BeginTransaction` additionally ignores whatever isolation-level argument it is given and always uses `ReadCommitted` ([BR-NH-11](../business-rules.md#br-nh-11-transactions-always-use-readcommitted-isolation)). This "no other isolation level" guarantee applies only to the standard `UnitOfWork.Begin()` entry point: the **stateless** strategy's `BeginTransaction(isolationLevel)` honors whatever level is passed to it directly (`session.BeginTransaction(isolationLevel)`), so a consumer calling `ISessionStrategy.BeginTransaction` directly on a stateless session — bypassing `UnitOfWork.Begin()` — can select a different isolation level.

`Begin` itself issues no SQL, but it is the first 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 transaction-in-progress guard and the clear/begin-transaction path taken when no transaction is already active.

```mermaid
flowchart TD
	Start(["Begin() called"]) --> Check{"Current transaction<br>still active?"}
	Check -->|Yes| Throw(["Throw InvalidOperationException"])
	Check -->|No| Clear["Session.Clear()<br>(stateful: discards cache; stateless: no-op)"]
	Clear --> BeginTx["Session.BeginTransaction(ReadCommitted)"]
	BeginTx --> Return(["Transaction active"])
```

## Failure and edge-case behavior
- Calling `Begin()` again while the previous transaction is still active (not yet committed or rolled back) throws `InvalidOperationException("There is a transaction already in progress.")`.
- After a successful [Commit](commit.md), the session stays open. Against NHibernate 5.2.7, NHibernate detaches the committed transaction from the session (`ConnectionManager.AfterTransaction()` sets `_transaction = null`), so a further `Begin()` on the same unit of work starts a new transaction and does not throw.
- After [Rollback](rollback.md), the unit of work disposes its session. Whether a later `Begin()` on that unit of work succeeds depends on the session strategy and is not confirmed by this library's source.
- Recommended practice: use a new unit of work for each transaction and dispose it when the work is done. This is a convention, not a restriction the library enforces.

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

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