Method BindResultsAsync
- Namespace
- YndigoBlue.Velocity.Engine
- Assembly
- YndigoBlue.Velocity.dll
BindResultsAsync<T>(BindingType, CancellationToken)
Asynchronous counterpart to BindResults<T>(BindingType). Binds query results to a
typed async sequence using the specified binding strategy, streaming rows from the live database
reader one at a time using await foreach.
public IAsyncEnumerable<T> BindResultsAsync<T>(BindingType bindingType, CancellationToken cancellationToken = default) where T : new()
Parameters
bindingTypeBindingTypeThe binding strategy to use (DirectMap, SnakeCase, CamelCase, or Attribute).
cancellationTokenCancellationTokenA token to cancel the underlying
ReadAsynccalls.
Returns
- IAsyncEnumerable<T>
A lazy IAsyncEnumerable<T> that streams typed objects from the database one row at a time.
Type Parameters
TThe type to bind each row to. Must have a parameterless constructor.
Examples
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public DateTime CreatedAt { get; set; }
}
var conn = new PostgreSqlDatasourceConnection
{
Hostname = "localhost",
Database = "app_db",
Username = "user",
Password = "password"
};
using (var manager = new Manager(conn))
{
var schema = manager.LoadSchema("public");
var usersTable = schema["users"];
var query = new Query()
.Select([usersTable["id"], usersTable["name"], usersTable["email"], usersTable["created_at"]])
.From(usersTable);
// Stream results with minimal memory usage, without blocking a thread while waiting on I/O
await using (ResultReader reader = await manager.SearchAsync(query))
{
// Use SnakeCase to map created_at -> CreatedAt
await foreach (User user in reader.BindResultsAsync<User>(BindingType.SnakeCase))
{
Console.WriteLine($"{user.Name} - {user.Email}");
}
}
}
Remarks
Returns a lazy async sequence — must be consumed inside the await using block.
This method uses yield return internally and does not materialise, fetch, or bind any rows until
the returned IAsyncEnumerable<T> is iterated with await foreach. Each iteration
issues a non-blocking ReadAsync(CancellationToken) call against the live
database reader, then binds that single row to a new instance of T before
yielding it — only one bound object is ever in memory at a time. Because the sequence reads directly
from the live reader, it must be fully iterated (or the enumerator disposed) before the
enclosing await using (ResultReader ...) block exits. Storing the sequence and iterating it
after the reader has been disposed will throw an InvalidOperationException or
ObjectDisposedException.
Passing a cancelled or later-cancelled cancellationToken causes the next
ReadAsync call to throw OperationCanceledException from within the await foreach,
stopping enumeration early. The ResultReader itself is unaffected and can still be disposed
normally afterwards.
Binding Types:
- DirectMap - Maps properties directly to columns with exact name matching (case-insensitive)
- SnakeCase - Converts C# PascalCase properties to snake_case database columns (e.g., FirstName -> first_name)
- CamelCase - Converts C# PascalCase properties to camelCase database columns (e.g., FirstName -> firstName)
- Attribute - Uses [VelocityField] attributes to map properties to custom column names