Repository navigation
Mise Resource Lifetimes
Readers and writers own the commands they execute. Connection and transaction ownership depends on how you construct them. Keep each resource alive until its operations and any stream enumeration have finished.
| Construction | Connection owner |
|---|---|
new DbReader(config: config) |
Reader |
new DbReader(connection: connection) |
Caller |
new DbWriter(config: config) |
Writer |
new DbWriter(connection: connection) |
Caller |
| Writer supplied with a transaction | Caller owns the transaction's connection |
A configuration-backed scope creates and opens its connection lazily. A supplied connection may be opened when needed, but Mise does not close or dispose it. Reader construction requires exactly one connection source.
await using var connection = new NpgsqlConnection(connectionString);
await using var reader = new DbReader(connection: connection);
...Declarations dispose in reverse order, so the reader ends before the caller-owned connection.
Each terminal asynchronously disposes the command it creates. Row terminals also dispose their database reader. ListAsync returns after consuming the rows; StreamAsync holds these resources until its enumerator ends or is disposed.
Exceptions from execution or disposal propagate. Returning Result<T> does not imply database exceptions are converted into failure cases.
Readers and writers permit one active operation at a time. Starting another operation on the same instance throws InvalidOperationException. This includes reader methods inherited by DbWriter, active streams, and transaction completion operations.
Do not run Task.WhenAll over calls sharing one reader or writer. Use separate scopes for independent concurrent operations, or await operations sequentially when they share a transaction.
Disposing a reader while an operation is active also throws. Finish or dispose a stream's enumerator before disposing its reader. See Streaming.
An enlisted transaction adapter must be disposed before its writer. The writer rejects disposal while that adapter remains active. With a unit of work, declare the writer first and the unit of work second:
await using var writer = new DbWriter(config: config);
await using var work = new UnitOfWork([writer.Transaction]);
...Commit or roll back before leaving the scope. A caller-supplied provider transaction becomes adapter-owned, while its connection remains caller-owned.
Partie's reader Provider owns its reader around downstream query work. Its writer transaction Provider owns the command writer and disposes the transaction before the writer through the outer unit of work.
Do not dispose provided readers or writers yourself, store them for later requests, or return a deferred stream that will enumerate after their Provider scope ends. Complete database work inside the handler or the surrounding step that owns the invocation.
See Transactions, Readers, and Mise in ASP.NET Core.
- Home
- Getting started
-
Package guides
- Partie
- Expo
-
Mise
- Database setup
- Table mappings
- Aliases
- Relationships
- Result rows
- SQL interpolation
- Queries
- Filtering
- Joins
- Aggregation
- Paging and ordering
- Subqueries
- Set operations
- Common table expressions
- Readers
- Streaming
- Writers
- Inserts
- Updates
- Deletes
- Returned rows
- Custom SQL and procedures
- Transactions
- Resource lifetimes
- Database dialects
- Diagnostics
- Integrations
- Recipes
- Reference
- Troubleshooting
- Development