.NET (ADO.NET)
Thunderduck.Data is an ADO.NET provider for .NET 8 and later. It works with
plain ADO.NET, Dapper and DataTable, and adds an async client for submitting,
waiting and paging yourself. It has no third-party dependencies.
dotnet add package Thunderduck.DataThe package is published on NuGet as
Thunderduck.Data (opens in a new tab). Versions,
release notes and other install commands (PackageReference, Paket) are on that
page.
Connection
Host=api.thunderduck.ai;Token=tdk_…Authenticate with an API token created in the console (Settings → API tokens). Optional keys:
Timeout: query timeout in seconds, default 300;PageSize: rows per fetch, default 1000;PollInterval: poll interval in ms, default 1000;Ssl: HTTPS, default true;Port.
Open() validates the token. After that, ConnectionString no longer contains
it.
Querying
using Thunderduck.Data;
await using var conn = new ThunderduckConnection($"Host=api.thunderduck.ai;Token={token}");
await conn.OpenAsync();
await using var cmd = conn.CreateCommand();
cmd.CommandText = "SELECT order_id, total_amount FROM sales_pg.public.orders WHERE total_amount > @min";
cmd.Parameters.AddWithValue("min", 100m);
await using var reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync())
Console.WriteLine($"{reader.GetInt64(0)}: {reader.GetDecimal(1)}");With Dapper:
var orders = await conn.QueryAsync<Order>(
"SELECT order_id AS Id, total_amount AS Total FROM sales_pg.public.orders WHERE total_amount > @min",
new { min = 100m });Addressing
Exactly as everywhere else — three parts, with the middle part depending on the catalog type:
SELECT * FROM sales_pg.public.orders LIMIT 100;
SELECT * FROM lake.default.events LIMIT 100;conn.GetSchema("Tables") lists your catalogs' tables. The Catalogs,
Schemas and Columns collections work the same way, with restrictions. These
calls read metadata Thunderduck already collected and run no SQL.
Cancellation
Cancelling the CancellationToken, calling DbCommand.Cancel() or hitting the
timeout each stop the wait and cancel the query on the server. Nothing keeps
running after your caller gives up.
Types
Values come back as the .NET type you'd expect:
| DuckDB | .NET |
|---|---|
| integer types | int, long, … |
| DECIMAL | decimal, exact |
| DATE | DateTime (or GetFieldValue<DateOnly>) |
| TIME | TimeSpan (or GetFieldValue<TimeOnly>) |
| TIMESTAMP | DateTime |
| TIMESTAMPTZ | DateTime in UTC (or GetFieldValue<DateTimeOffset>) |
| UUID | Guid |
| BLOB | byte[] |
Nested types (LIST, STRUCT, MAP) come back as JSON strings. You can also read
them with GetFieldValue<JsonElement>().
Limits
- Each query runs as a job, so expect seconds before the first row. Don't use it for tight request/response loops.
- There are no transactions, and
RecordsAffectedis -1. - Parameters are inlined into the SQL by the client as typed DuckDB literals; the server doesn't bind them.
- Results are read from the stored result. Stored results expire after a day,
and reading an expired one throws
ThunderduckExceptionwithKind = ResultExpired.