SqlBulkPro 1.0.3
dotnet add package SqlBulkPro --version 1.0.3
NuGet\Install-Package SqlBulkPro -Version 1.0.3
<PackageReference Include="SqlBulkPro" Version="1.0.3" />
<PackageVersion Include="SqlBulkPro" Version="1.0.3" />
<PackageReference Include="SqlBulkPro" />
paket add SqlBulkPro --version 1.0.3
#r "nuget: SqlBulkPro, 1.0.3"
#:package SqlBulkPro@1.0.3
#addin nuget:?package=SqlBulkPro&version=1.0.3
#tool nuget:?package=SqlBulkPro&version=1.0.3
SqlBulkPro
High-performance SQL Server bulk operations for .NET 8/9+
SqlBulkPro provides blazing-fast bulk insert, upsert, update, delete, and sync operations for SQL Server using SqlBulkCopy + MERGE. Works seamlessly with EF Core and Dapper/ADO.NET.
Why SqlBulkPro?
- Measured against EF Core
SaveChangeson one SQL Server 2022 container (see benchmarks; about 4× to 10× lower mean time in that run) - 🎯 Production-ready with proper locking, transactions, and concurrency handling
- 🔧 Simple API - single method calls, no complex configuration
- 🔄 True upsert using SQL Server MERGE (solves EF Core issue #4526 open since 2016)
- Commercial license — developer $149/year or team $599/year for up to 10 developers. The license key is emailed after purchase. Competitor prices are cited with sources in docs/comparison.md
Quick Start
Installation
dotnet add package SqlBulkPro
EF Core
using SqlBulkPro;
await dbContext.BulkInsertAsync(products);
// Upsert: insert new, update existing
await dbContext.BulkUpsertAsync(products);
// Bulk update
await dbContext.BulkUpdateAsync(products);
// Bulk delete
await dbContext.BulkDeleteAsync(products);
// Keep explicit identity values. The library sets IDENTITY_INSERT on for the write, then off.
await dbContext.BulkInsertAsync(productsWithIds, new BulkOptions { KeepIdentity = true });
Dapper/ADO.NET
using SqlBulkPro;
using Microsoft.Data.SqlClient;
using var connection = new SqlConnection(connectionString);
// Bulk insert with attributes or fluent config
await connection.BulkInsertAsync(products);
// Bulk upsert
await connection.BulkUpsertAsync(products, config => config
.ToTable("Products", "dbo")
.HasKey(nameof(Product.Id)));
// Sync, including a transaction overload. DeleteMissingRows is required.
await connection.BulkSyncAsync(products, config => config
.WithOptions(opt => opt.DeleteMissingRows = true));
Features
Operations
- ✅ BulkInsert - Fast bulk insertion with identity propagation
- ✅ BulkUpsert - Insert new rows, update existing (
MERGE, orUPDATEthenINSERTwhenStrategyisUpdateInsert) - ✅ BulkUpdate - Update matching rows only, including an ADO.NET transaction overload
- ✅ BulkDelete - Delete matching rows, including an ADO.NET transaction overload
- ✅ BulkSync - Upsert plus delete of missing rows, on
DbContext,DbSet, andSqlConnection
Capabilities
- 🔑 Composite key support
- 🆔 Identity value propagation back to entities
- 🔄 Value converter support (EF Core)
- 🔒 Proper HOLDLOCK hints to prevent race conditions
- 📦 Configurable batch sizes
- ⏱️ Timeout control
- 🔐 Transaction support (respects ambient transactions)
- 🎛️ Column filtering (include/exclude specific columns)
- 🚀 Zero-reflection hot paths (compiled expressions)
Configuration
var options = new BulkOptions
{
KeyColumns = new List<string> { "ProductCode", "Region" }, // Custom keys
ColumnsToIgnore = new List<string> { "LastModified" }, // Don't update
InsertOnlyColumns = new List<string> { "CreatedAt" }, // Insert only
BatchSize = 10000, // Batch size
TimeoutSeconds = 60, // Timeout
UseHoldLock = true, // Prevent races
ReturnIdentities = true, // Get IDs back
UpdateOnlyIfChanged = true, // Skip identical rows
Strategy = MergeStrategy.Merge // or UpdateInsert
};
await dbContext.BulkUpsertAsync(products, options);
Performance
One BenchmarkDotNet 0.14.0 run on 9 November 2026. Host: Ubuntu 24.04, 4-core Intel Xeon, 15 GiB RAM, .NET 9.0.22. SQL Server 2022 ran in Docker on that same machine (mcr.microsoft.com/mssql/server:2022-latest). Job: 1 warmup and 3 measured iterations. The speedup column is EF mean / SqlBulkPro mean.
| Operation | Rows | EF Core mean | SqlBulkPro mean | EF / SqlBulkPro |
|---|---|---|---|---|
| Insert | 1,000 | 96.96 ms | 17.84 ms | 5.4 |
| Insert | 10,000 | 517.66 ms | 54.06 ms | 9.6 |
| Insert | 100,000 | 3,768.89 ms | 554.19 ms | 6.8 |
| Update vs upsert | 1,000 | 106.31 ms | 28.11 ms | 3.8 |
| Update vs upsert | 10,000 | 614.58 ms | 109.36 ms | 5.6 |
| Update vs upsert | 100,000 | 5,226.42 ms | 856.17 ms | 6.1 |
Insert compares AddRange + SaveChanges with BulkInsertAsync. Update vs upsert compares UpdateRange + SaveChanges with BulkUpsertAsync after the rows already exist. Standard deviations and the full method notes are in docs/benchmarks.md.
Licensing
SqlBulkPro requires a commercial license for production use.
Trial Mode
- Limited to 1,000 rows per operation
- A 14-day in-memory trial check runs outside Development.
DOTNET_ENVIRONMENT=Development,ASPNETCORE_ENVIRONMENT=Development, or an attached debugger skips that check. The row cap still applies
License options
The license key is emailed after purchase. Support: dbdavis1990apple@gmail.com
- Developer: $149 per developer per year
- Team (up to 10 developers): $599 per year
Issuing and the token format are documented in docs/licensing.md.
Activating your license
Use either method. The key is one long string; paste it unchanged.
Environment variable. The library reads SQLBULKPRO_LICENSE_KEY the first time a bulk method runs, unless SqlBulkProLicense.Register was already called. A missing or rejected variable leaves the library in trial mode (1,000 rows, and a console banner). It does not throw.
export SQLBULKPRO_LICENSE_KEY="paste-the-key-here"
$env:SQLBULKPRO_LICENSE_KEY = "paste-the-key-here"
Code. SqlBulkProLicense.Register checks the signature and expiry immediately. An empty, altered, or expired key throws, and a rejected key does not replace one that was already accepted. Call it at startup.
using SqlBulkPro;
var builder = WebApplication.CreateBuilder(args);
SqlBulkProLicense.Register(builder.Configuration["SqlBulkPro:LicenseKey"]!);
var app = builder.Build();
app.Run();
appsettings.json (leave the real key out of source control):
{
"SqlBulkPro": {
"LicenseKey": ""
}
}
User secrets for local development:
dotnet user-secrets set "SqlBulkPro:LicenseKey" "paste-the-key-here"
SqlBulkPro__LicenseKey is the environment-variable form of that configuration entry. It is only used if your startup code passes it to Register. The library itself reads SQLBULKPRO_LICENSE_KEY, not SqlBulkPro__LicenseKey.
If neither Register nor SQLBULKPRO_LICENSE_KEY is set, the library also checks sqlbulkpro.lic next to the application and license.key under the SqlBulkPro folder of the application-data directory. Details are in docs/licensing.md.
Free Tier
For bulk insert only, use the free SqlBulkPro.Lite package (MIT licensed):
dotnet add package SqlBulkPro.Lite
Documentation
Requirements
- .NET 8.0 or later
- SQL Server 2016 or later
- Entity Framework Core 8.0+ (for EF integration)
Support
- Email: dbdavis1990apple@gmail.com
- NuGet: https://www.nuget.org/profiles/danieldavisdev
- Docs in this repository, including licensing
License
Commercial license required for production use. See LICENSE.txt and EULA.txt.
SqlBulkPro - Built for developers who need production-grade bulk operations without the enterprise price tag.
| 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 was computed. 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. |
-
net8.0
- Microsoft.Data.SqlClient (>= 5.2.2)
- Microsoft.EntityFrameworkCore.Relational (>= 8.0.11)
- Portable.BouncyCastle (>= 1.9.0)
- System.Linq.Async (>= 6.0.1)
-
net9.0
- Microsoft.Data.SqlClient (>= 5.2.2)
- Microsoft.EntityFrameworkCore.Relational (>= 8.0.11)
- Portable.BouncyCastle (>= 1.9.0)
- System.Linq.Async (>= 6.0.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
1.0.3 adds ADO.NET transaction overloads for update and delete, DbSet.BulkSyncAsync, conditional updates, the UpdateInsert strategy, and catalog-based identity detection.