Skip to content

Mise Transactions

SteffenBlake edited this page Sep 28, 2026 · 1 revision

Transactions

DbWriter.Transaction supplies an ITxn adapter for the writer's database transaction. Enlist it in a UnitOfWork, or commit and roll it back explicitly. Merely creating a writer does not establish a transaction boundary.

Coordinate writes directly

await using var writer = new DbWriter(config: config);
await using var work = new UnitOfWork([writer.Transaction]);

try
{
    var command = new PostgreSqlCommandBuilder()
        .InsertInto($"{CourseTbl.Table:raw}")
        .Columns($"title, capacity")
        .Values($"{title}, {capacity}");

    await writer.ExecuteAsync(command, cancellationToken);
    await work.CommitAsync(cancellationToken);
}
catch
{
    await work.RollbackAsync(CancellationToken.None);
    throw;
}

config implements IDbConfig. Requesting writer.Transaction creates the adapter, and the first database operation starts the provider transaction. Reads through the same writer also use that transaction. Add more operations before committing when they must share the boundary.

The declaration order matters: work is disposed before writer, so it disposes the transaction adapter first. If rollback itself throws, handle that cleanup failure according to your application's exception policy; a simple catch like the example can surface the rollback exception.

Lazy and eager transaction start

The adapter is lazy. An unused adapter can commit or roll back without opening a database connection. To start the provider transaction immediately, call:

ITxn transaction = await writer.BeginTransactionAsync(cancellationToken);

This returns the same adapter exposed by writer.Transaction. Give it one owner for completion and disposal. The writer's transaction lifecycle is not reset to start a second independent transaction after completion; create a new writer scope for the next transaction.

Use an existing transaction

var providerTransaction = await connection.BeginTransactionAsync(cancellationToken);
await using var writer = new DbWriter(
    connection: connection,
    transaction: providerTransaction
);
await using var work = new UnitOfWork([writer.Transaction]);

...

The supplied transaction must belong to the supplied connection. A transaction can supply its own connection when the connection argument is omitted. Do not combine a supplied transaction with configuration for a different owned connection.

Transaction ownership transfers to the writer's adapter. The caller still owns the connection. Complete and dispose the unit of work before disposing the writer and connection; do not also give the transaction an independent disposal owner.

Partie command outcomes

Partie's UnitOfWorkPartie commits successful and deprecated results, and rolls back failure results, exceptions, and cancellation. The Mise database Providers supply an ITxn and its paired writer for command handlers.

Use the provided writer and return your application outcome. Avoid completing its transaction inside the handler when the surrounding unit of work owns that decision. See Mise in ASP.NET Core and Partie unit of work.

Boundary limits

Rollback covers changes in the enlisted database transaction, not unrelated connections or external side effects. A unit of work coordinating several resources commits them in sequence; it is not an atomic distributed transaction.

Always choose a completion outcome before disposing a directly managed UnitOfWork. Its disposal rejects an unfinished unit of work. See Resource lifetimes.

Clone this wiki locally