EF.CH 0.0.2

There is a newer version of this package available.
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
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="EF.CH" Version="0.0.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="EF.CH" Version="0.0.2" />
                    
Directory.Packages.props
<PackageReference Include="EF.CH" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add EF.CH --version 0.0.2
                    
#r "nuget: EF.CH, 0.0.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package EF.CH@0.0.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=EF.CH&version=0.0.2
                    
Install as a Cake Addin
#tool nuget:?package=EF.CH&version=0.0.2
                    
Install as a Cake Tool

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 ReplacingMergeTree with 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

Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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