Repository navigation
Mise Readers
DbReader executes query builders on one connection scope. Choose a terminal based on whether the application needs all rows, one row, one value, or an existence check.
| Method | Return shape | No matching rows |
|---|---|---|
ListAsync<T> |
Result<IReadOnlyList<T>> |
Successful empty list |
FirstOrNotFoundAsync<T> |
Result<T> |
NotFound |
ScalarAsync<T> |
Result<T?> |
Successful default(T)
|
ExistsAsync |
Result<bool> |
Successful false
|
StreamAsync<T> |
IAsyncEnumerable<T> |
No yielded items |
Row terminals require T : IRow<T>. See Result rows. Scalar and existence reads do not require a mapped row.
var query = new PostgreSqlQueryBuilder()
.Select($"{CourseTbl.CourseIdCol:raw}")
.Select($"{CourseTbl.TitleCol:raw}")
.Select($"{CourseTbl.CapacityCol:raw}")
.From($"{CourseTbl.Table:raw}");
var result = await reader.ListAsync<CourseRow>(query, cancellationToken);
if (result.IsSuccess(out var courses))
{
foreach (var course in courses)
{
Console.WriteLine(course.Title);
}
}ListAsync buffers the mapped rows before returning. The command and database reader are disposed by the terminal. Use Streaming when the caller should process rows incrementally.
query.Where($"{CourseTbl.CourseIdCol:raw} = {courseId}");
Result<CourseRow> course = await reader.FirstOrNotFoundAsync<CourseRow>(
query,
cancellationToken
);The terminal reads the first row and ignores later rows. It does not enforce uniqueness. Use a unique predicate or deliberate ordering when several matches are possible. It requests ADO.NET SingleRow behavior, which is a provider hint rather than a SQL uniqueness check.
var titleQuery = new PostgreSqlQueryBuilder()
.Select($"{CourseTbl.TitleCol:raw}")
.From($"{CourseTbl.Table:raw}")
.Where($"{CourseTbl.CourseIdCol:raw} = {courseId}");
Result<string?> title = await reader.ScalarAsync<string>(titleQuery, cancellationToken);Only the first cell is returned. No row and SQL NULL both produce default(T). For reference types this is null. For value types, pass a nullable type such as long? if you need null rather than zero for an absent value.
The non-null value is cast to T, not generally converted. Match the provider's returned type. For example, PostgreSQL COUNT(*) returns long; requesting int can fail with a cast exception.
var existsQuery = new PostgreSqlQueryBuilder()
.Select($"1")
.From($"{CourseTbl.Table:raw}")
.Where($"{CourseTbl.CourseIdCol:raw} = {courseId}");
Result<bool> exists = await reader.ExistsAsync(existsQuery, cancellationToken);ExistsAsync checks whether the query yields a row. A query selecting COUNT(*) yields a row even when the count is zero, so it is not an appropriate existence query.
Pass the operation's token to each terminal. Provider, mapping, cancellation, and disposal exceptions propagate to your caller; Mise does not automatically wrap them in an Error result. Expected empty-result behavior follows the table above.
Each reader allows one active operation. Finish a read before starting another on the same instance. See Resource lifetimes for connection ownership and Custom SQL and procedures for reader behavior flags.
- 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