SQL Server, 5,000 rows

Measured with the DbExtensions.Benchmark   project in etlbox.demo. Release build, local SQL Server, catalog demo. The Dapper baseline is one Execute per row — the pattern you write when Dapper has no bulk API.

OperationMethodSecondsvs Dapper
InsertDapper (row by row)7.875—
InsertSqlBulkCopy0.020388×
InsertBulkInsert0.069114×
UpdateDapper (row by row)7.735—
UpdateBulkUpdate0.42418×
DeleteDapper (row by row)7.485—
DeleteBulkDelete0.27128×
MergeDapper (select + insert or update)11.603—
MergeBulkMerge0.28441×

Read the table as order of magnitude, not a guarantee. Your hardware, indexes, network, and batch size will move the seconds. The shape stays the same: a loop of statements does not compete with a bulk loader.

What this does not claim

  • Not 99% copied from another vendor. These times are from the script above, on this machine, at 5,000 rows.
  • BulkInsert is not faster than raw SqlBulkCopy. SqlBulkCopy is the SQL Server insert path. DbExtensions wraps it and maps your objects — that costs a few extra milliseconds and buys a one-liner plus update/delete/merge on the same API.
  • The Dapper baseline is intentionally naive. No explicit transaction, no TVP, no handmade SqlBulkCopy. That is what most Dapper code does for writes. If you already wrap a loop in a transaction, the gap shrinks but does not disappear on SQL Server inserts.

How to rerun

cd DbExtensions.Benchmark
dotnet run -c Release
dotnet run -c Release -- --rows 5000 --cs "Data Source=localhost;..."

Without a license key, keep --rows at or below 4999. Drop etlbox.lic next to the project to measure larger sets. Connection string: --cs, or env DBEXTENSIONS_BENCHMARK_CS, or the same localhost default as the other DbExtensions demos.

The same bulk engine is also used from Entity Framework via EFBox. A separate demo, EF_BenchmarkExample, compared SaveChanges with EFBox on SQL Server (100,000 inserts):

MethodSeconds
EF Core AddRange + SaveChanges31.5
EFBox BulkInsert7.5

That is a different stack (change tracker vs bulk), so it is not comparable 1:1 with the Dapper table. It is included because those were the only published numbers in the demo repo before DbExtensions.Benchmark existed.

Why bulk wins

Each Dapper Execute is parse + execute + round-trip. BulkInsert on SQL Server streams rows through SqlBulkCopy. BulkUpdate / BulkDelete / BulkMerge batch work instead of issuing one command per object. That is also why SqlBulkCopy alone is not enough if you need updates, deletes, merge, or another database.