Skip to content

Mise Writers

SteffenBlake edited this page Sep 28, 2026 · 1 revision

Writers

DbWriter executes command builders and exposes a transaction adapter. It also inherits DbReader, so reads made through the writer share its connection and active transaction.

Choose a write terminal

Method Return shape No returned rows
ExecuteAsync Result<int> affected count Count depends on the statement and provider
ExecuteScalarAsync<T> Result<T?> first cell Successful default(T)
ReturningListAsync<T> Result<IReadOnlyList<T>> Successful empty list
ReturningFirstOrNotFoundAsync<T> Result<T> NotFound

The returning row terminals require T : IRow<T>. They execute a statement that produces a result set; they do not add a RETURNING or OUTPUT clause themselves.

Execute a command

var command = new PostgreSqlCommandBuilder()
    .Update($"{CourseTbl.Table:raw}")
    .Set($"capacity = {capacity}")
    .Where($"{CourseTbl.CourseIdCol:raw} = {courseId}");

Result<int> affected = await writer.ExecuteAsync(command, cancellationToken);

Zero affected rows is a successful count, not an automatic failure. Provider-specific commands and procedures may report another count convention, including a negative count when none is available.

See Inserts, Updates, and Deletes for building commands. Use Returned rows when the application needs data from the write.

Read within a write transaction

var query = new PostgreSqlQueryBuilder()
    .Select($"{CourseTbl.CourseIdCol:raw}")
    .Select($"{CourseTbl.TitleCol:raw}")
    .Select($"{CourseTbl.CapacityCol:raw}")
    .From($"{CourseTbl.Table:raw}")
    .Where($"{CourseTbl.CourseIdCol:raw} = {courseId}");

var course = await writer.FirstOrNotFoundAsync<CourseRow>(query, cancellationToken);

This uses the writer's connection and its enlisted transaction. A separate reader can use another connection and observe a different state. Transaction isolation and database locking still determine concurrent behavior.

Complete and dispose

Request writer.Transaction to enlist a lazy transaction adapter, or call BeginTransactionAsync to start it immediately. A direct writer without either transaction setup uses the provider's ordinary statement behavior.

Complete and dispose an enlisted transaction before disposing the writer. Partie's provided writers have that ownership managed by the command unit of work. See Transactions and Mise in ASP.NET Core.

Each writer allows one active operation across both its read and write methods. Await operations sequentially when they share the writer. Provider, mapping, cancellation, and disposal exceptions propagate; the result wrapper does not catch them automatically.

Clone this wiki locally