Benchmark project comparing EF Core (tracking and no-tracking) vs Dapper using BenchmarkDotNet and PostgreSQL.
This repository provides a reproducible benchmarking harness to compare data access performance between EF Core and Dapper under equivalent query scenarios. It focuses on read-heavy workloads and highlights the trade-offs between productivity and raw speed.
- Benchmark scenarios:
ById,PagedList,FilterByStatus - EF Core with tracking and
AsNoTracking - Dapper queries for the same scenarios
- Dataset seeding with configurable size
- BenchmarkDotNet configuration (warmup, iterations, exporters)
- Docker Compose for local PostgreSQL
- .NET 10
- BenchmarkDotNet
- EF Core 10 + Npgsql
- Dapper
- PostgreSQL 16
This project is a console benchmark tool, not an HTTP API. There is no error contract or response schema.
None. This project does not expose HTTP endpoints.
docker compose up -dThe compose file exposes PostgreSQL on port 5433.
$env:BENCHMARK_DB = "Host=localhost;Port=5433;Database=benchmarks;Username=postgres;Password=postgres"dotnet run --project .\benchmarks\DataAccess.Benchmarks -- --seed --count 50000 --resetdotnet run -c Release --project .\benchmarks\DataAccess.Benchmarks -- --filter *.\scripts\seed.ps1 -Count 50000 -Reset
.\scripts\bench.ps1 -Filter * -Min 10000 -Configuration ReleaseRun a smaller dataset:
.\scripts\seed.ps1 -Count 10000 -ResetRun only the ById benchmarks:
.\scripts\bench.ps1 -Filter *ById* -Configuration ReleaseEnvironment (2026-02-02)
- Windows 11 25H2 (10.0.26200.7623)
- AMD Ryzen 7 6800H (8c/16t)
- .NET SDK 10.0.102 / Runtime 10.0.2
- PostgreSQL 16 (Docker, port 5433)
- Dataset: 50,000 rows
Parameters used in summary
- Paged:
PageSize=50,PageNumber=5 - Status:
Status=2,PageSize=50
| Scenario | ORM | Mean (ms) | StdDev | Alloc (KB) | Difference (%) |
|---|---|---|---|---|---|
| ById | EF Core (Tracking) | 0.743 | 0.086 | 40.66 | +48.3 |
| ById | EF Core (NoTracking) | 0.622 | 0.018 | 40.71 | +24.2 |
| ById | Dapper | 0.501 | 0.026 | 1.68 | 0 |
| Paged | EF Core (Tracking) | 0.672 | 0.036 | 86.05 | +33.6 |
| Paged | EF Core (NoTracking) | 0.640 | 0.044 | 58.96 | +27.2 |
| Paged | Dapper | 0.503 | 0.020 | 12.47 | 0 |
| Status | EF Core (Tracking) | 0.716 | 0.049 | 87.21 | +31.0 |
| Status | EF Core (NoTracking) | 0.893 | 0.174 | 60.08 | +63.3 |
| Status | Dapper | 0.547 | 0.040 | 12.48 | 0 |
Change in Mean from Cycle 1 to Cycle 2 (positive = slower).
| Scenario | ORM | Mean Cycle 1 (ms) | Mean Cycle 2 (ms) | Change (%) |
|---|---|---|---|---|
| ById | EF Core (Tracking) | 0.575 | 0.743 | +29.2 |
| ById | EF Core (NoTracking) | 0.561 | 0.622 | +10.9 |
| ById | Dapper | 0.484 | 0.501 | +3.5 |
| Paged | EF Core (Tracking) | 0.749 | 0.672 | -10.3 |
| Paged | EF Core (NoTracking) | 0.740 | 0.640 | -13.5 |
| Paged | Dapper | 0.511 | 0.503 | -1.6 |
| Status | EF Core (Tracking) | 0.731 | 0.716 | -2.1 |
| Status | EF Core (NoTracking) | 0.731 | 0.893 | +22.2 |
| Status | Dapper | 0.518 | 0.547 | +5.6 |
There is no absolute rule. Choose the tool based on latency goals, workload volume, and team productivity.