Dtde.EntityFramework
1.0.0
dotnet add package Dtde.EntityFramework --version 1.0.0
NuGet\Install-Package Dtde.EntityFramework -Version 1.0.0
<PackageReference Include="Dtde.EntityFramework" Version="1.0.0" />
<PackageVersion Include="Dtde.EntityFramework" Version="1.0.0" />
<PackageReference Include="Dtde.EntityFramework" />
paket add Dtde.EntityFramework --version 1.0.0
#r "nuget: Dtde.EntityFramework, 1.0.0"
#:package Dtde.EntityFramework@1.0.0
#addin nuget:?package=Dtde.EntityFramework&version=1.0.0
#tool nuget:?package=Dtde.EntityFramework&version=1.0.0
<div align="center">
DTDE - Distributed Temporal Data Engine
Transparent horizontal sharding and temporal versioning for Entity Framework Core
π 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 |
| π― 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 | 100+ 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. Create Your DbContext
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>(entity =>
{
entity.ShardBy(c => c.Region); // Enable sharding by Region
});
}
}
3. Configure Services
builder.Services.AddDbContext<AppDbContext>(options =>
{
options.UseSqlServer(connectionString);
options.UseDtde(dtde =>
{
dtde.AddShard(s => s.WithId("EU").WithShardKeyValue("EU").WithTier(ShardTier.Hot));
dtde.AddShard(s => s.WithId("US").WithShardKeyValue("US").WithTier(ShardTier.Hot));
dtde.AddShard(s => s.WithId("APAC").WithShardKeyValue("APAC").WithTier(ShardTier.Hot));
});
});
4. Use It!
// Queries are automatically routed to correct shard(s)
var allCustomers = await _context.Customers.ToListAsync(); // Queries all shards
var euCustomers = await _context.Customers
.Where(c => c.Region == "EU")
.ToListAsync(); // Only queries EU shard
// Inserts are automatically routed based on shard key
_context.Customers.Add(new Customer { Name = "Acme Corp", Region = "US" }); // Routes to US shard
await _context.SaveChangesAsync();
π 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, DateInterval.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
Composite Sharding
Combine strategies for complex scenarios:
modelBuilder.Entity<Order>(entity =>
{
entity.ShardBy(o => o.Region)
.ThenByDate(o => o.OrderDate);
});
β±οΈ 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 |
|---|---|
| CPU | 12th Gen Intel Core i9-12900H (14 cores, 20 threads) |
| Runtime | .NET 9.0, RyuJIT AVX2 |
| Database | SQLite (file-based, separate DBs per benchmark) |
| Framework | BenchmarkDotNet 0.15.0 |
Key Results
| Query Type | Records | Single Table | Sharded | Improvement |
|---|---|---|---|---|
| Point Lookup | 100K | 143.9 ns | 146.5 ns | ~Same |
| Date Range (1 month) | 100K | 16,103 Β΅s | 3,596 Β΅s | 4.5x faster |
| Region Scan | 100K | 3,659 Β΅s | 1,786 Β΅s | 2.0x faster |
| Count | 50K | 3,534 Β΅s | 26.0 Β΅s | 136x faster |
Key insight: Sharded queries benefit significantly from partition pruning β queries that target a specific shard key value only scan relevant partitions.
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
β οΈ 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 all tests (101 tests)
dotnet test
# Run with coverage
dotnet test --collect:"XPlat Code Coverage"
# Run specific test project
dotnet test tests/Dtde.Core.Tests/
π Documentation
- Full Documentation β Complete guides, API reference, and tutorials
- Quick Start Guide β Get running in 5 minutes
- Sharding Guide β Deep dive into sharding strategies
- Temporal Guide β Temporal versioning explained
- API Reference β Complete API documentation
- Architecture β Design decisions and architecture
π€ 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
- Built with Entity Framework Core
- Inspired by temporal database concepts and bi-temporal data modeling
- Benchmarked with BenchmarkDotNet
<div align="center">
Made with β€οΈ for the .NET community
β Star on GitHub Β· π Report Bug Β· π¬ Discussions
</div>
| Product | Versions 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. |
-
net10.0
- Dtde.Abstractions (>= 1.0.0)
- Dtde.Core (>= 1.0.0)
- Microsoft.EntityFrameworkCore (>= 10.0.1)
- Microsoft.EntityFrameworkCore.Relational (>= 10.0.1)
- Microsoft.Extensions.DependencyInjection (>= 10.0.1)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.1)
- Microsoft.Extensions.Logging (>= 10.0.1)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.1)
- Microsoft.Extensions.Options (>= 10.0.1)
-
net8.0
- Dtde.Abstractions (>= 1.0.0)
- Dtde.Core (>= 1.0.0)
- Microsoft.EntityFrameworkCore (>= 8.0.12)
- Microsoft.EntityFrameworkCore.Relational (>= 8.0.12)
- Microsoft.Extensions.DependencyInjection (>= 8.0.1)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.2)
- Microsoft.Extensions.Logging (>= 8.0.1)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.3)
- Microsoft.Extensions.Options (>= 8.0.2)
-
net9.0
- Dtde.Abstractions (>= 1.0.0)
- Dtde.Core (>= 1.0.0)
- Microsoft.EntityFrameworkCore (>= 9.0.11)
- Microsoft.EntityFrameworkCore.Relational (>= 9.0.11)
- Microsoft.Extensions.DependencyInjection (>= 9.0.11)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.11)
- Microsoft.Extensions.Logging (>= 9.0.11)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.11)
- Microsoft.Extensions.Options (>= 9.0.11)
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 |
|---|---|---|
| 1.0.0 | 117 | 12/12/2025 |
See https://github.com/yohasacura/dtde/blob/main/CHANGELOG.md for release notes.