ClickHouse.Driver 1.0.0: The Official .NET Client Hits Stable

Alex Soffronow Pagonidis
Mar 2, 2026 · 7 minutes read

We are proud to announce the release of ClickHouse.Driver 1.0.0, the first stable release of the official .NET client for ClickHouse. This has been a ground-up effort: new API design, full type coverage, package signing, and OpenTelemetry integration, all built on top of the community project we adopted last year. Starting with 1.0.0, the public API is stable. The package follows semver, and we won't break your code between minor versions.

This release also comes bundled with a big documentation upgrade and 40+ practical usage examples.

Road to 1.0.0 #

The 1.0.0 release builds on two major pre-releases:

0.8.0 focused on packaging, configuration, and observability:

  • Added support for .NET 10
  • NuGet package signing and strong naming
  • ClickHouseClientSettings for structured configuration
  • Logging and diagnostics support via ILoggerFactory
  • EnableDebugMode for low-level network tracing
  • Improved OpenTelemetry/ActivitySource integration

0.9.0 focused on type support:

  • Added support for BFloat16, Time/Time64, Geometry types (LineString, MultiLineString, Polygon)
  • Reading Dynamic now supports all underlying types
  • Improved JSON and FixedString support

The New ClickHouseClient API #

1.0.0 introduces ClickHouseClient, a new primary API that replaces the ADO.NET classes for most use cases. It's thread-safe, singleton-friendly, and a lot less ceremony than ClickHouseConnection.

using var client = new ClickHouseClient("Host=localhost");

// DDL
await client.ExecuteNonQueryAsync("CREATE TABLE logs (ts DateTime, msg String) ENGINE = MergeTree ORDER BY ts");

// Binary bulk insert (replaces ClickHouseBulkCopy)
await client.InsertBinaryAsync("logs", ["ts", "msg"], rows); // rows: IEnumerable<object[]>

// Query
var parameters = new ClickHouseParameterCollection();
parameters.AddParameter("since", DateTime.UtcNow.AddDays(-7));
using var reader = await client.ExecuteReaderAsync("SELECT * FROM logs WHERE ts > {since:DateTime}", parameters);

// Scalar
var count = await client.ExecuteScalarAsync("SELECT count() FROM logs");

Key methods:

MethodWhat it does
ExecuteNonQueryAsyncDDL/DML (CREATE, INSERT, ALTER, DROP)
ExecuteScalarAsyncReturns a single result
ExecuteReaderAsyncStream results via ClickHouseDataReader
InsertBinaryAsyncHigh-performance bulk insert
ExecuteRawResultAsyncRaw result stream in arbitrary format (e.g. save Parquet directly to a file)
InsertRawStreamAsyncInsert from stream (CSV, JSON, Parquet, etc.)
PingAsyncCheck server connectivity
CreateConnection()Get a ClickHouseConnection for ORM compatibility

Per-query settings (query ID, custom server settings, roles, bearer token) are passed via the new QueryOptions and InsertOptions classes. The ADO.NET layer (ClickHouseConnection / ClickHouseCommand) remains fully supported for ORM integration with Dapper and linq2db.

Note: ClickHouseBulkCopy is now deprecated. Use client.InsertBinaryAsync(table, columns, rows) instead.

What Else is New in 1.0.0 #

Automatic parameter type extraction from SQL #

No more specifying the type twice. The driver now extracts parameter types directly from your SQL:

// Before: type specified in both the SQL and the parameter
command.CommandText = "SELECT {dt:DateTime('Europe/Amsterdam')}";
command.AddParameter("dt", "DateTime('Europe/Amsterdam')", value);

// After: type extracted from SQL automatically
command.CommandText = "SELECT {dt:DateTime('Europe/Amsterdam')}";
command.AddParameter("dt", value);

JWT authentication, roles, custom HTTP headers #

JWT authentication, ClickHouse roles, and custom HTTP headers are now supported at the client level, with per-query overrides via QueryOptions. Check out the examples on GitHub: JWT example, roles example, and custom headers example.

POCO serialization for JSON columns #

Write plain C# objects directly to typed JSON columns. Properties are serialized using the column's type hints, with automatic inference for unhinted properties:

client.RegisterJsonSerializationType<UserEvent>();

await client.InsertBinaryAsync("events", ["data"], rows);

Control serialization using attributes: use [ClickHouseJsonPath("custom.path")] for custom paths and [ClickHouseJsonIgnore] to exclude properties. See the JSON type example.

Mid-stream exception detection #

ClickHouse 25.11+ can signal exceptions mid-stream via the X-ClickHouse-Exception-Tag header. The driver now detects these and throws a proper ClickHouseServerException with the error message, instead of a generic EndOfStreamException.

QBit vector type #

QBit stores vectors in a compact transposed binary format; quantization granularity is chosen at query time rather than at insert time, letting you trade precision for speed without re-ingesting data. See the QBit similarity search example.

Query ID auto-generation #

Every query now gets a unique ID automatically when one isn't explicitly set, making it easier to trace queries in server logs.

Additional improvements #

  • Binary data in String/FixedString columns — write byte[], ReadOnlyMemory<byte>, or Stream; read back with the ReadStringsAsByteArrays setting.
  • Full support for Dynamic type binary writing.
  • AddParameter() convenience method on ClickHouseParameterCollection.

Breaking Changes #

1.0.0 includes breaking changes to clean up the API surface. Here's a summary:

Dropped .NET Framework / .NET Standard. The library now targets net6.0, net8.0, net9.0, and net10.0 only. If you're on .NET Framework, stay on the previous version or migrate to .NET 6.0+.

DateTime behavior changed for columns without explicit timezone. Columns like DateTime (without a timezone parameter) now return DateTime with Kind=Unspecified, preserving the stored wall-clock time exactly. Writing also changed: DateTime.Kind is now respected. Utc and Local values maintain their instant, while Unspecified values are treated as wall-clock time.

Migration: Use explicit timezones in column definitions (DateTime('UTC')) or apply timezones after reading.

Removed UseServerTimezone. No longer needed since timezone-less columns return Unspecified values. ServerTimezone moved from ClickHouseConnection to ClickHouseCommand (extracted from response headers).

JSON write mode default changed from Binary to String. JSON data is now serialized via System.Text.Json and parsed server-side. This removes the need for the client to parse JSON structure into binary column format before sending. The server handles parsing directly from the JSON string. Binary writing now only works with POCOs.

Migration: JSON string or JsonNode writing still works, but the JSON string will be parsed on the server instead of the client. This could lead to subtle changes in paths without type hints, e.g., values previously parsed as Int32 may be parsed as Int64.

Other changes:

  • Removed feature discovery query from OpenAsync (no more SELECT version() on connect)
  • Several helper/extension methods made internal
  • See the full release notes for details and migration guidance

Getting Started #

Install via NuGet:

dotnet add package ClickHouse.Driver

Quick start:

using ClickHouse.Driver;

using var client = new ClickHouseClient("Host=localhost;Port=8123");

// Create a table
await client.ExecuteNonQueryAsync("""
    CREATE TABLE IF NOT EXISTS events (
        timestamp DateTime('UTC'),
        event_type String,
        payload JSON
    ) ENGINE = MergeTree ORDER BY timestamp
    """);

// Insert data
var rows = new List<object[]>
{
	    ([DateTime.UtcNow, "click", """{"page": "/home"}"""]),
	    ([DateTime.UtcNow, "view", """{"page": "/about"}"""]),
};
await client.InsertBinaryAsync("events", ["timestamp", "event_type", "payload"], rows);

// Query
using var reader = await client.ExecuteReaderAsync("SELECT * FROM events");
while (reader.Read())
{
    Console.WriteLine($"{reader.GetDateTime(0)} | {reader.GetString(1)} | {reader.GetString(2)}");
}

We'd love your feedback! Open an issue or find us on ClickHouse Community Slack.

Get started today

Interested in seeing how ClickHouse works on your data? Get started with ClickHouse Cloud in minutes and receive $300 in free credits.
Share this post

Subscribe to our newsletter

Stay informed on feature releases, product roadmap, support, and cloud offerings!
Loading form...

Recent posts