Table of Contents

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

bindingType BindingType

The binding strategy to use (DirectMap, SnakeCase, CamelCase, or Attribute).

cancellationToken CancellationToken

A token to cancel the underlying ReadAsync calls.

Returns

IAsyncEnumerable<T>

A lazy IAsyncEnumerable<T> that streams typed objects from the database one row at a time.

Type Parameters

T

The 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