Dtde.Core 1.0.1

dotnet add package Dtde.Core --version 1.0.1
                    
NuGet\Install-Package Dtde.Core -Version 1.0.1
                    
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="Dtde.Core" Version="1.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Dtde.Core" Version="1.0.1" />
                    
Directory.Packages.props
<PackageReference Include="Dtde.Core" />
                    
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 Dtde.Core --version 1.0.1
                    
#r "nuget: Dtde.Core, 1.0.1"
                    
#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 Dtde.Core@1.0.1
                    
#: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=Dtde.Core&version=1.0.1
                    
Install as a Cake Addin
#tool nuget:?package=Dtde.Core&version=1.0.1
                    
Install as a Cake Tool

<div align="center">

DTDE - Distributed Temporal Data Engine

Transparent horizontal sharding and temporal versioning for Entity Framework Core

.NET EF Core License Tests GitHub

πŸ“š Documentation Β· πŸš€ Quick Start Β· πŸ’‘ Samples Β· πŸ“Š Benchmarks

</div>


Overview

DTDE is a NuGet package that adds transparent horizontal sharding and optional temporal versioning to Entity Framework Core applications. Write standard LINQ queries β€” DTDE handles data distribution, query routing, and result merging automatically.

// Standard EF Core LINQ - DTDE routes queries transparently
var euCustomers = await db.Customers
    .Where(c => c.Region == "EU")
    .ToListAsync();  // Automatically queries only EU shard

// Point-in-time queries for temporal entities
var ordersLastMonth = await db.ValidAt<Order>(DateTime.Today.AddMonths(-1))
    .Where(o => o.Status == "Completed")
    .ToListAsync();

✨ Key Features

Feature Description
πŸ”€ Transparent Sharding Distribute data across tables or databases invisibly
⏱️ Temporal Versioning Track entity history with point-in-time queries
πŸ”— Cross-Shard Transactions ACID transactions spanning multiple database shards
🎯 Property Agnostic Use ANY property names for sharding keys and temporal boundaries
πŸ“ EF Core Native Works with standard LINQ β€” no special query syntax required
⚑ Multiple Strategies Date-based, hash-based, range-based, or composite sharding
πŸ—„οΈ Hot/Warm/Cold Tiers Support for data tiering across storage tiers
βœ… Fully Tested 400+ unit and integration tests

πŸ“¦ Installation

# All-in-one package (recommended)
dotnet add package Dtde.EntityFramework

# Or install individual packages
dotnet add package Dtde.Abstractions  # Core interfaces
dotnet add package Dtde.Core          # Core implementations
dotnet add package Dtde.EntityFramework  # EF Core integration

Requirements: .NET 8.0+ / 9.0+ / 10.0+, Entity Framework Core 8.0+ / 9.0+ / 10.0+


πŸš€ Quick Start

1. Define your entity

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
    public string Region { get; set; } = string.Empty;  // shard key
    public DateTime CreatedAt { get; set; }
}

2. Inherit DtdeDbContext and declare the shard key in OnModelCreating

public class AppDbContext : DtdeDbContext
{
    public DbSet<Customer> Customers => Set<Customer>();

    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<Customer>()
            .ShardBy(c => c.Region);
    }
}

3. One call in Program.cs

builder.Services.AddDtdeDbContext<AppDbContext>(
    db => db.UseSqlite("Data Source=app.db"),
    dtde => dtde.AddShards("EU", "US", "APAC"));

That's it β€” three logical shards over a single SQLite connection. Need separate databases per shard? Swap AddShards(...) for one AddShard("EU", euConnStr) per region.

4. Use standard LINQ β€” DTDE routes for you

// Insert: routed by Region
db.Customers.Add(new Customer { Name = "Acme Corp", Region = "US" });
await db.SaveChangesAsync();

// Query without filter: fans out across all shards, merges results
var all = await db.Customers.ToListAsync();

// Query with a Where on the shard key: pruned to one shard
var euOnly = await db.Customers
    .Where(c => c.Region == "EU")
    .ToListAsync();

That's the entire mental model. See the Getting started guide for date / hash sharding, temporal entities, and per-shard databases.


πŸ”€ Sharding Strategies

DTDE supports multiple sharding strategies to match your data access patterns:

Property-Based Sharding

Distribute data by a property value (region, tenant, category):

modelBuilder.Entity<Customer>(entity =>
{
    entity.ShardBy(c => c.Region);
});

Use cases: Multi-region deployments, GDPR compliance, data residency

Date-Based Sharding

Partition data by date for time-series workloads:

modelBuilder.Entity<Transaction>(entity =>
{
    entity.ShardByDate(t => t.TransactionDate, DateShardInterval.Month);
});

Use cases: Financial transactions, audit logs, metrics, event sourcing

Hash-Based Sharding

Even distribution across shards using consistent hashing:

modelBuilder.Entity<UserProfile>(entity =>
{
    entity.ShardByHash(u => u.UserId, shardCount: 8);
});

Use cases: High-volume data, preventing hotspots, horizontal scaling

Manual Sharding

Map an entity to pre-existing tables managed by your DBA or a SQL project:

modelBuilder.Entity<Order>(entity =>
{
    entity.UseManualSharding(config =>
    {
        config.AddTable("dbo.Orders_2023", o => o.OrderDate.Year == 2023);
        config.AddTable("dbo.Orders_2024", o => o.OrderDate.Year == 2024);
        config.MigrationsEnabled = false;
    });
});

Use cases: Brownfield databases, regulatory schemas you don't control, archive tables.


⏱️ Temporal Versioning

Track entity history and query data at any point in time:

Enable Temporal Tracking

modelBuilder.Entity<Contract>(entity =>
{
    entity.HasTemporalValidity(
        validFrom: nameof(Contract.ValidFrom),
        validTo: nameof(Contract.ValidTo));
});

Temporal Queries

// Current data
var current = await _context.ValidAt<Contract>(DateTime.UtcNow).ToListAsync();

// Historical data (as of a specific date)
var historical = await _context.ValidAt<Contract>(new DateTime(2024, 1, 1)).ToListAsync();

// All versions (bypass temporal filtering)
var allVersions = await _context.AllVersions<Contract>()
    .Where(c => c.ContractNumber == "C-001")
    .OrderBy(c => c.ValidFrom)
    .ToListAsync();

// Data within a date range
var rangeData = await _context.ValidBetween<Contract>(startDate, endDate).ToListAsync();

Temporal Operations

// Add with effective date
_context.AddTemporal(contract, effectiveFrom: DateTime.UtcNow);

// Create new version (closes old, opens new)
var newVersion = _context.CreateNewVersion(existing, changes, effectiveDate);

// Terminate (close validity)
_context.Terminate(contract, terminationDate: DateTime.UtcNow);

await _context.SaveChangesAsync();

πŸ”„ Mixed Usage: Sharded + Regular Entities

DTDE works seamlessly alongside regular EF Core entities:

public class AppDbContext : DtdeDbContext
{
    public DbSet<Customer> Customers => Set<Customer>();   // Sharded
    public DbSet<Contract> Contracts => Set<Contract>();   // Temporal
    public DbSet<AuditLog> AuditLogs => Set<AuditLog>();   // Regular EF Core
}
Entity Configuration Behavior
ShardBy() configured Queries routed by shard key
HasTemporalValidity() configured Temporal filtering applied
No special configuration Standard EF Core entity
Direct DbSet<T> access Bypasses DTDE filtering

πŸ—„οΈ Shard Tiers

Organize shards by access frequency for cost optimization:

dtde.AddShard(s => s
    .WithId("2024-current")
    .WithTier(ShardTier.Hot)         // Frequently accessed, recent data
    .WithConnectionString(fastStorage));

dtde.AddShard(s => s
    .WithId("2023-archive")
    .WithTier(ShardTier.Cold)        // Archived, rarely accessed
    .WithConnectionString(cheapStorage)
    .AsReadOnly());                   // Prevent accidental writes
Tier Description Typical Storage
Hot Frequently accessed, recent data SSD, Premium SQL
Warm Less frequently accessed Standard SQL
Cold Archived, rarely accessed Archive storage
Archive Long-term retention Cold storage

βš™οΈ Configuration Options

Fluent API

options.UseDtde(dtde =>
{
    // Entity configuration
    dtde.ConfigureEntity<Order>(e => e.ShardByDate(o => o.OrderDate));

    // Shard definitions
    dtde.AddShard(s => s.WithId("primary").WithConnectionString("..."));

    // Load from configuration file
    dtde.AddShardsFromConfig("shards.json");

    // Default temporal context
    dtde.SetDefaultTemporalContext(() => DateTime.UtcNow);

    // Performance tuning
    dtde.SetMaxParallelShards(10);

    // Diagnostics
    dtde.EnableDiagnostics();
});

JSON Configuration (shards.json)

{
  "shards": [
    {
      "shardId": "2024-q4",
      "name": "Q4 2024 Data",
      "connectionString": "Server=...;Database=Data2024Q4",
      "tier": "Hot",
      "dateRangeStart": "2024-10-01",
      "dateRangeEnd": "2024-12-31",
      "isReadOnly": false,
      "priority": 100
    },
    {
      "shardId": "2024-archive",
      "name": "2024 Archive",
      "connectionString": "Server=...;Database=Archive2024",
      "tier": "Cold",
      "isReadOnly": true,
      "priority": 10
    }
  ]
}

πŸ“Š Performance Benchmarks

Comprehensive benchmarks comparing single table vs sharded approaches:

Test Environment

Component Specification
OS Windows 11 (10.0.26200.7462)
CPU 12th Gen Intel Core i9-12900H (14 cores, 20 threads)
Runtime .NET 9.0.10, RyuJIT AVX2
SDK .NET SDK 10.0.101
Database SQLite (file-based, separate DBs per benchmark)
Framework BenchmarkDotNet v0.15.0

Key Results

Query Type Records Single Table Sharded Improvement
Point Lookup (by ID) 100K 153.1 ns 145.7 ns ~Same
Date Range (1 month) 100K 16,257 Β΅s 3,555 Β΅s 4.6x faster
Region Scan (single) 100K 3,638 Β΅s 1,774 Β΅s 2.1x faster
Region Scan (all) 100K 5,535 Β΅s 2,014 Β΅s 2.7x faster
Count (all records) 100K 7,873 Β΅s 1,330 Β΅s 5.9x faster
Count (all records) 50K 3,387 Β΅s 24.1 Β΅s 141x faster

Concurrent Access Performance

Scenario Threads Single Table Sharded Improvement
Parallel Aggregation 4 1,668 Β΅s 546 Β΅s 3.1x faster
Parallel Aggregation 8 2,043 Β΅s 696 Β΅s 2.9x faster
Parallel Reads (diff regions) 4 1,822 Β΅s 542 Β΅s 3.4x faster
Parallel Reads (diff regions) 8 2,479 Β΅s 758 Β΅s 3.3x faster

Write Operations

Operation Batch Size Single Table Sharded Notes
Batch Update 100 414.5 Β΅s 317.7 Β΅s 23% faster
Batch Update 1000 388.8 Β΅s 299.5 Β΅s 23% faster
Batch Delete 100 326.8 Β΅s 302.4 Β΅s ~Same

Key insight: Sharded queries benefit significantly from partition pruning β€” queries that target a specific shard key value only scan relevant partitions. Concurrent workloads show even greater improvements due to reduced contention.

When to Use Sharding

βœ… Good candidates:

  • Large datasets (millions+ rows)
  • Time-series / temporal data
  • Multi-tenant applications
  • Geographic distribution requirements
  • High write throughput needs
  • Hot/cold data patterns
  • Concurrent read-heavy workloads

⚠️ Consider carefully:

  • Small datasets (<100K rows)
  • Random access patterns
  • Complex cross-entity joins
  • Simple CRUD applications
# Run benchmarks
cd benchmarks/Dtde.Benchmarks
dotnet run -c Release

πŸ—οΈ Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    Your Application                          β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                   Dtde.EntityFramework                       β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚DtdeDbContextβ”‚  β”‚Query Engine β”‚  β”‚   Update Engine     β”‚  β”‚
β”‚  β”‚  - ValidAt  β”‚  β”‚ - Rewriter  β”‚  β”‚  - VersionManager   β”‚  β”‚
β”‚  β”‚  - AllVersionsβ”‚ β”‚ - Executor  β”‚  β”‚  - ShardRouter     β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                      Dtde.Core                               β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚  Metadata   β”‚  β”‚  Sharding   β”‚  β”‚     Temporal        β”‚  β”‚
β”‚  β”‚  Registry   β”‚  β”‚  Strategies β”‚  β”‚     Context         β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                   Dtde.Abstractions                          β”‚
β”‚         Interfaces β€’ Contracts β€’ Exceptions                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ“ Project Structure

dtde/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ Dtde.Abstractions/        # Core interfaces and contracts
β”‚   β”œβ”€β”€ Dtde.Core/                # Core implementations
β”‚   └── Dtde.EntityFramework/     # EF Core integration
β”œβ”€β”€ tests/
β”‚   β”œβ”€β”€ Dtde.Core.Tests/          # Unit tests
β”‚   β”œβ”€β”€ Dtde.EntityFramework.Tests/
β”‚   └── Dtde.Integration.Tests/
β”œβ”€β”€ samples/                       # Sample applications
β”‚   β”œβ”€β”€ Dtde.Sample.WebApi/       # Basic getting started
β”‚   β”œβ”€β”€ Dtde.Samples.RegionSharding/   # Property-based sharding
β”‚   β”œβ”€β”€ Dtde.Samples.DateSharding/     # Date-based sharding
β”‚   β”œβ”€β”€ Dtde.Samples.HashSharding/     # Hash-based sharding
β”‚   β”œβ”€β”€ Dtde.Samples.MultiTenant/      # Multi-tenancy
β”‚   └── Dtde.Samples.Combined/         # Mixed strategies
β”œβ”€β”€ benchmarks/
β”‚   └── Dtde.Benchmarks/          # Performance benchmarks
└── docs/                         # Documentation (MkDocs)

πŸ’‘ Sample Projects

Explore working examples for each sharding strategy:

Sample Strategy Use Case
Dtde.Sample.WebApi Basic Getting started
Dtde.Samples.RegionSharding Property-based Multi-region data residency
Dtde.Samples.DateSharding Date-based Time-series, audit logs
Dtde.Samples.HashSharding Hash-based Even data distribution
Dtde.Samples.MultiTenant Tenant-based SaaS multi-tenancy
Dtde.Samples.Combined Mixed Complex enterprise scenarios

πŸ§ͺ Testing

# Run the full suite (~403 tests across Core, EntityFramework, Integration)
dotnet test -c Release

# Run with coverage
dotnet test -c Release --collect:"XPlat Code Coverage"

# Run a single project
dotnet test tests/Dtde.Core.Tests/Dtde.Core.Tests.csproj -c Release

πŸ“š Documentation


🀝 Contributing

Contributions are welcome! Please read our Contributing Guide before submitting PRs.

# Clone and build
git clone https://github.com/yohasacura/dtde.git
cd dtde
dotnet build
dotnet test

See also:


πŸ“„ License

This project is licensed under the MIT License β€” see the LICENSE file for details.


πŸ™ Acknowledgments


<div align="center">

Made with ❀️ for the .NET community

⭐ Star on GitHub Β· πŸ› Report Bug Β· πŸ’¬ Discussions

</div>

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  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 (1)

Showing the top 1 NuGet packages that depend on Dtde.Core:

Package Downloads
Dtde.EntityFramework

Transparent horizontal sharding and bi-temporal versioning for Entity Framework Core. Write standard LINQ queries; DTDE handles routing, partition pruning, point-in-time reads, and two-phase cross-shard transactions automatically. This is the recommended package for application developers.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.1 117 5/14/2026
1.0.0 169 12/12/2025