A typed, layered .NET library for ClickHouse -- schema modeling, migrations, querying, and more.
- Immutable record-based type system -- 40+ ClickHouse types including Geo, JSON, Nested, and LowCardinality
- Fluent schema modeling via
SchemaBuilderwith automatic snake_case conventions - Auto-diffing migrations with safety policies (blocks destructive ops by default)
- LINQ-style query builder with
IQueryable<T>support and SQL rendering - CLI tool for scaffolding, migrations, SQL generation, and status checks
- ASP.NET Core DI integration with hosted migration service
- Dual targeting: net8.0 + net10.0
| Guide | Description |
|---|---|
| Architecture & Design | Package layering, data flow, and core design patterns |
| Type System | Complete type reference, CLR mappings, compound types, and custom mappings |
| Migration Workflow | End-to-end migration guide, safety policies, locking, and hooks |
| Query Builder | LINQ and fluent query APIs, Ch.* functions, window functions, and ClickHouse extensions |
| ClickHouse Patterns | Engine selection, materialized views, dictionaries, TTL, indexes, and codecs |
| Troubleshooting | Common issues, error recovery, and FAQ |
public class Event
{
public DateTime Timestamp { get; set; }
public string UserId { get; set; } = "";
public string EventType { get; set; } = "";
public int Value { get; set; }
}using CH.Toolkit.Modeling;
var schema = new SchemaBuilder()
.Table<Event>()
.MergeTree()
.OrderBy(e => e.Timestamp, e => e.UserId)
.PartitionBy(e => e.Timestamp, "toYYYYMM")
.Build("analytics");using CH.Toolkit.Migrations.Engine;
var engine = new MigrationEngine(new MigrationEngineOptions
{
ConnectionString = "Host=localhost;Port=9000",
Database = "analytics"
});
await engine.MigrateAsync(migrationsAssembly);using CH.Toolkit.Data;
await using var ctx = new ClickHouseContext(new ClickHouseContextOptions
{
ConnectionString = "Host=localhost;Port=9000",
Database = "analytics"
});
// Typed set with LINQ-style queries
var events = ctx.Set<Event>();
var recent = await events.AsQueryable()
.Where(e => e.Timestamp > DateTime.UtcNow.AddDays(-7))
.ToListAsync();
// Bulk insert
await events.InsertAsync(newEvents);
// Raw SQL
var results = await ctx.QueryAsync<Event>(
"SELECT * FROM analytics.event WHERE event_type = 'click'");| Package | Description | Dependencies |
|---|---|---|
CH.Toolkit.Types |
Type system (40+ ClickHouse types) | none |
CH.Toolkit.Sql |
SQL AST + ClickHouse renderer | Types |
CH.Toolkit.Schema |
Schema model + DDL compiler | Types, Sql |
CH.Toolkit.Modeling |
Fluent schema builder | Schema, Query, Types |
CH.Toolkit.Query |
LINQ/fluent query builder | Sql, Types |
CH.Toolkit.Data |
Context + typed sets | Query |
CH.Toolkit.Introspection |
Schema introspection from live DB | Schema, Types, Sql |
CH.Toolkit.Migrations |
Differ, codegen, runner | Schema, Sql, Introspection, Types |
CH.Toolkit.Cli |
CLI tool | Introspection, Migrations |
CH.Toolkit.DependencyInjection |
DI extensions | Migrations |
CH.Toolkit |
Meta-package (all above) | all |
Install globally:
dotnet tool install -g CH.Toolkit.CliCommands:
| Command | Description |
|---|---|
chsharp init |
Initialize a new project with config file |
chsharp add-migration |
Generate a migration from schema diff |
chsharp migrate |
Apply pending migrations |
chsharp scaffold |
Reverse-engineer schema from an existing database |
chsharp sql |
Generate SQL from migration files |
chsharp status |
Show pending/applied migration status |
Global options: --connection, --database, --cluster
using CH.Toolkit.DependencyInjection;
builder.Services.AddClickHouse(
connectionString: "Host=localhost;Port=9000",
database: "analytics");
// Register ClickHouseContext for querying
builder.Services.AddClickHouseContext(
connectionString: "Host=localhost;Port=9000",
database: "analytics");
// Auto-migrate on startup
builder.Services.AddClickHouseMigrateOnStartup(
migrationsAssembly: typeof(Program).Assembly);Or configure with full options:
builder.Services.AddClickHouse(new MigrationEngineOptions
{
ConnectionString = "Host=localhost;Port=9000",
Database = "analytics",
ClusterName = "my_cluster"
});dotnet build CH.Toolkit.sln
dotnet test CH.Toolkit.sln --filter "FullyQualifiedName!~IntegrationTests"Integration tests require Docker:
dotnet test tests/CH.Toolkit.IntegrationTests/CH.Toolkit.IntegrationTests.csproj