Transactions - pengdows/pengdows.crud GitHub Wiki

Table of Contents

Transactions

pengdows.crud provides full transaction support via a dedicated TransactionContext, which implements IDatabaseContext and ensures that all SQL operations executed within its scope are atomic and isolated.

Creating a Transaction

To create a transaction, request a TransactionContext from an existing DatabaseContext:

await using var tx = await context.BeginTransactionAsync();

By default, this will use the minimum required isolation level:

  • `READ COMMITTED` for write operations
  • `REPEATABLE READ` for read-only connections
You can optionally pass an explicit isolation level:
await using var tx = await context.BeginTransactionAsync(IsolationLevel.Serializable);

Executing Commands in a Transaction

Within a transaction, any `EntityHelper` or `SqlContainer` created from the `TransactionContext` will participate in the open transaction:

var helper = new EntityHelper<User, Guid>(tx, serviceProvider);
await helper.CreateAsync(user);

var sc = tx.CreateSqlContainer();
// build and execute query...

The connection will remain open for the duration of the transaction, and will not be automatically closed after each command.

Committing or Rolling Back

Transactions must be explicitly committed:

await tx.CommitAsync();

If the `TransactionContext` is disposed without being committed, it will automatically roll back:

await using var tx = await context.BeginTransactionAsync();
// ... something fails or throws
// No need to call Rollback explicitly

Minimum Isolation Levels

pengdows.crud enforces a minimum isolation level policy to ensure safe and repeatable behavior across databases:

  • `READ COMMITTED` is the lowest allowed for write transactions
  • `REPEATABLE READ` is enforced for read-only transactions
  • `CockroachDB` uses `SERIALIZABLE` always, and ignores the requested level

Read/Write Mode Enforcement

The context will throw an exception if you attempt to:

  • Run a `NonQuery` or `Scalar` on a read-only connection
  • Run a `Reader` on a write-only connection
This is enforced at the method level and exists to assist developers using CQRS or read replicas. It is not a security mechanism.

Nested Transactions

Nested TransactionContext creation is supported, but the inner transaction is isolated and independent. There is no ambient or ambient-suppressed behavior like in Entity Framework.

Example

await using var tx = await context.BeginTransactionAsync();
try
{
    var userHelper = new EntityHelper<User, Guid>(tx, sp);
    await userHelper.CreateAsync(newUser);

    var logHelper = new EntityHelper<AuditLog, Guid>(tx, sp);
    await logHelper.CreateAsync(newLog);

    await tx.CommitAsync();
}
catch
{
    // auto-rollback happens on dispose
    throw;
}

Related Pages

⚠️ **GitHub.com Fallback** ⚠️