EF.CH
0.0.2
See the version list below for details.
dotnet add package EF.CH --version 0.0.2
NuGet\Install-Package EF.CH -Version 0.0.2
<PackageReference Include="EF.CH" Version="0.0.2" />
<PackageVersion Include="EF.CH" Version="0.0.2" />
<PackageReference Include="EF.CH" />
paket add EF.CH --version 0.0.2
#r "nuget: EF.CH, 0.0.2"
#:package EF.CH@0.0.2
#addin nuget:?package=EF.CH&version=0.0.2
#tool nuget:?package=EF.CH&version=0.0.2
EF.CH - Entity Framework Core Provider for ClickHouse
An Entity Framework Core provider for ClickHouse, built on the ClickHouse.Driver ADO.NET driver.
Features
- LINQ to ClickHouse SQL - Full query translation with ClickHouse-specific optimizations
- MergeTree Engine Family - MergeTree, ReplacingMergeTree, SummingMergeTree, AggregatingMergeTree, CollapsingMergeTree
- Rich Type System - Arrays, Maps, Tuples, Nested types, Enums, IPv4/IPv6, DateTime64
- Materialized Views - LINQ-based and raw SQL definitions
- EF Core Migrations - DDL generation with ClickHouse-specific clauses
- DELETE Support - Lightweight and mutation-based strategies
- Dictionaries - In-memory key-value stores with dictGet translation
- Scaffolding - Reverse engineering with C# enum generation
Quick Start
// 1. Install the package
// dotnet add package EF.CH
// 2. Create your entity
public class Order
{
public Guid Id { get; set; }
public DateTime OrderDate { get; set; }
public string CustomerId { get; set; } = string.Empty;
public decimal Total { get; set; }
}
// 3. Create your DbContext
public class MyDbContext : DbContext
{
public DbSet<Order> Orders => Set<Order>();
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseClickHouse("Host=localhost;Database=mydb");
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Order>(entity =>
{
entity.HasKey(e => e.Id);
entity.UseMergeTree(x => new { x.OrderDate, x.Id }); // Required: ORDER BY
entity.HasPartitionByMonth(x => x.OrderDate); // Optional: partitioning
});
}
}
// 4. Use it
await using var context = new MyDbContext();
await context.Database.EnsureCreatedAsync();
context.Orders.Add(new Order
{
Id = Guid.NewGuid(),
OrderDate = DateTime.UtcNow,
CustomerId = "customer-123",
Total = 99.99m
});
await context.SaveChangesAsync();
var recentOrders = await context.Orders
.Where(o => o.OrderDate > DateTime.UtcNow.AddDays(-7))
.ToListAsync();
Installation
dotnet add package EF.CH
Requirements:
- .NET 10.0+
- ClickHouse 22.0+
- EF Core 10.0
ClickHouse Concepts for EF Core Developers
If you're coming from SQL Server or PostgreSQL, ClickHouse works differently. Understanding these differences is essential:
Every Table Needs an ENGINE
Unlike SQL Server where tables "just work", ClickHouse requires you to specify a table engine. The MergeTree family is most common:
// This is REQUIRED - there's no default engine
entity.UseMergeTree(x => new { x.OrderDate, x.Id });
No ACID Transactions
ClickHouse uses eventual consistency. SaveChanges() batches INSERTs but there's no rollback on failure. Design your application accordingly.
No Row-Level UPDATE
ClickHouse doesn't support efficient UPDATE statements. Attempting to update an entity throws NotSupportedException. Instead:
- Use
ReplacingMergeTreewith a version column for "last write wins" semantics - Use delete-and-reinsert patterns for infrequent updates
Batch-Oriented, Not Row-at-a-Time
ClickHouse is optimized for bulk inserts (thousands of rows). Single-row inserts work but aren't efficient. Batch your writes when possible.
No Auto-Increment
There's no IDENTITY or auto-increment. Use Guid or application-generated IDs:
public Guid Id { get; set; } = Guid.NewGuid();
No Foreign Key Enforcement
ClickHouse doesn't enforce referential integrity. Foreign keys are your application's responsibility.
Table Engines
Choose the right engine for your use case:
| Engine | Use Case | Configuration |
|---|---|---|
| MergeTree | General purpose, append-only | entity.UseMergeTree(x => x.Id) |
| ReplacingMergeTree | Deduplication by key with version | entity.UseReplacingMergeTree(x => x.Version, x => x.Id) |
| SummingMergeTree | Auto-sum numeric columns | entity.UseSummingMergeTree(x => new { x.Date, x.ProductId }) |
| AggregatingMergeTree | Pre-aggregated state | entity.UseAggregatingMergeTree(x => x.Key) |
| CollapsingMergeTree | Row cancellation with sign | entity.UseCollapsingMergeTree(x => x.Sign, x => x.Key) |
| VersionedCollapsingMergeTree | Out-of-order row cancellation | entity.UseVersionedCollapsingMergeTree(x => x.Sign, x => x.Version, x => x.Key) |
See docs/engines/ for detailed documentation on each engine.
Type Mappings
| .NET Type | ClickHouse Type |
|---|---|
int, long, short, sbyte |
Int32, Int64, Int16, Int8 |
uint, ulong, ushort, byte |
UInt32, UInt64, UInt16, UInt8 |
float, double |
Float32, Float64 |
decimal |
Decimal(18, 4) |
string |
String |
bool |
Bool |
Guid |
UUID |
DateTime |
DateTime64(3) |
DateTimeOffset |
DateTime64(3) with timezone |
DateOnly |
Date |
TimeOnly |
Time |
T[], List<T> |
Array(T) |
Dictionary<K,V> |
Map(K, V) |
enum |
Enum8 or Enum16 (auto-selected) |
See docs/types/ for the complete type mapping reference.
Key Differences from SQL Server/PostgreSQL
| Feature | SQL Server/PostgreSQL | ClickHouse |
|---|---|---|
| Transactions | Full ACID | Eventual consistency |
| UPDATE | Efficient row updates | Not supported - use ReplacingMergeTree |
| DELETE | Immediate | Lightweight (marks) or mutation (async rewrite) |
| Auto-increment | IDENTITY, SERIAL |
Not available - use UUID |
| Foreign Keys | Enforced constraints | Application-level only |
| Indexes | B-tree, hash, etc. | Primary key (ORDER BY) only |
| Insert Pattern | Row-at-a-time OK | Batch thousands of rows |
| Use Case | OLTP | OLAP/Analytics |
Table Options
// Partitioning - improves query performance and data management
entity.HasPartitionByMonth(x => x.CreatedAt); // PARTITION BY toYYYYMM()
entity.HasPartitionByDay(x => x.EventDate); // PARTITION BY toYYYYMMDD()
// TTL - automatic data expiration
entity.HasTtl("CreatedAt + INTERVAL 90 DAY");
// Sampling - for approximate queries on large datasets
entity.HasSampleBy("intHash32(UserId)");
DELETE Operations
// Via change tracker (lightweight delete by default)
var entity = await context.Orders.FindAsync(id);
context.Orders.Remove(entity);
await context.SaveChangesAsync();
// Bulk delete
await context.Orders
.Where(o => o.OrderDate < cutoffDate)
.ExecuteDeleteAsync();
// Configure mutation-based delete
options.UseClickHouse("...", o => o.UseDeleteStrategy(ClickHouseDeleteStrategy.Mutation));
Materialized Views
// LINQ-based (type-safe)
modelBuilder.Entity<HourlySummary>(entity =>
{
entity.UseSummingMergeTree(x => new { x.Hour, x.ProductId });
entity.AsMaterializedView<HourlySummary, Order>(
query: orders => orders
.GroupBy(o => new { Hour = o.OrderDate.Date, o.ProductId })
.Select(g => new HourlySummary
{
Hour = g.Key.Hour,
ProductId = g.Key.ProductId,
OrderCount = g.Count(),
TotalRevenue = g.Sum(o => o.Total)
}),
populate: false);
});
Dictionaries
ClickHouse dictionaries are in-memory key-value stores for fast lookups:
// Define dictionary entity with marker interface
public class CountryLookup : IClickHouseDictionary
{
public ulong Id { get; set; }
public string Name { get; set; } = string.Empty;
}
// Configure in OnModelCreating
entity.AsDictionary<CountryLookup, Country>(cfg => cfg
.HasKey(x => x.Id)
.FromTable()
.UseHashedLayout()
.HasLifetime(300));
// Use in LINQ queries - translates to dictGet()
var orders = db.Orders
.Select(o => new {
o.Id,
CountryName = db.CountryDict.Get(o.CountryId, c => c.Name)
});
Layouts: Flat, Hashed, ComplexKeyHashed, Cache, Direct
Documentation
| Topic | Description |
|---|---|
| Getting Started | Installation and first project |
| ClickHouse Concepts | Key differences from RDBMS |
| Table Engines | MergeTree family guide |
| Type Mappings | Complete type reference |
| Features | Materialized views, partitioning, TTL, etc. |
| Migrations | EF Core migrations with ClickHouse |
| Scaffolding | Reverse engineering |
| Limitations | What doesn't work |
Samples
| Sample | Description |
|---|---|
| QuickStartSample | Minimal working example |
| MigrationSample | EF Core migrations |
| KeylessSample | Keyless entities for append-only data |
| ReplacingMergeTreeSample | Deduplication patterns |
| SummingMergeTreeSample | Auto-aggregation with SummingMergeTree |
| CollapsingMergeTreeSample | Row cancellation with sign column |
| MaterializedViewSample | Real-time aggregation |
| ArrayTypeSample | Working with arrays |
| MapTypeSample | Working with Map(K, V) dictionaries |
| EnumTypeSample | ClickHouse enum type mapping |
| PartitioningSample | Table partitioning strategies |
| QueryModifiersSample | Final(), Sample(), WithSettings() |
| DeleteStrategiesSample | Lightweight vs mutation deletes |
| OptimizeTableSample | Programmatic OPTIMIZE TABLE |
| DictionarySample | In-memory dictionary lookups |
| DictionaryJoinSample | Dictionaries as JOIN replacement |
License
MIT License - see LICENSE for details.
Acknowledgments
- ClickHouse.Driver - The ADO.NET driver this provider builds on
- EntityFrameworkCore.ClickHouse - Reference implementation
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- ClickHouse.Driver (>= 0.9.0)
- Microsoft.EntityFrameworkCore.Design (>= 10.0.0)
- Microsoft.EntityFrameworkCore.Relational (>= 10.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 8.0.7 | 78 | 1/8/2026 |
| 8.0.6 | 84 | 1/8/2026 |
| 8.0.5 | 95 | 1/7/2026 |
| 8.0.4 | 81 | 1/7/2026 |
| 8.0.3 | 81 | 1/6/2026 |
| 8.0.2 | 84 | 12/31/2025 |
| 8.0.1 | 81 | 12/31/2025 |
| 8.0.0 | 164 | 12/23/2025 |
| 0.0.25 | 82 | 1/8/2026 |
| 0.0.24 | 82 | 1/7/2026 |
| 0.0.23 | 79 | 1/7/2026 |
| 0.0.22 | 85 | 1/6/2026 |
| 0.0.21 | 76 | 1/6/2026 |
| 0.0.20 | 84 | 1/1/2026 |
| 0.0.19 | 80 | 12/31/2025 |
| 0.0.18 | 174 | 12/23/2025 |
| 0.0.17 | 147 | 12/21/2025 |
| 0.0.16 | 148 | 12/21/2025 |
| 0.0.15 | 148 | 12/21/2025 |
| 0.0.14 | 150 | 12/21/2025 |
| 0.0.13 | 121 | 12/21/2025 |
| 0.0.12 | 118 | 12/20/2025 |
| 0.0.11 | 126 | 12/20/2025 |
| 0.0.10 | 108 | 12/20/2025 |
| 0.0.9 | 111 | 12/20/2025 |
| 0.0.8 | 207 | 12/19/2025 |
| 0.0.7 | 265 | 12/19/2025 |
| 0.0.6 | 260 | 12/18/2025 |
| 0.0.5 | 261 | 12/17/2025 |
| 0.0.4 | 260 | 12/17/2025 |
| 0.0.3 | 264 | 12/15/2025 |
| 0.0.2 | 146 | 12/14/2025 |
| 0.0.1 | 434 | 12/9/2025 |
| 0.0.0 | 202 | 12/4/2025 |