Last Updated on July 31, 2025 by Aram
In this article, we will be highlighting a key feature of Dapper Plus, the Bulk Extensions, the fastest way to perform bulk operations in .NET.
But before going further with Dapper Plus, let’s see what is Dapper?
Dapper is a lightning-fast micro ORM for .NET that gives you full control over your SQL while mapping query results to C# objects.
Dapper sits on top of ADO.NET and it allows you to write raw SQL commands and queries (buffered and non-buffered) and send them directly to the database leveraging the IDbConnection via sync and async API calls.
Dapper entails minimal overhead and provides maximum performance for queries.
Limitations of Dapper
But when it comes to handling large datasets, writing INSERT, UPDATE, or DELETE commands using Dapper, one row at a time, can kill performance.
That’s where Dapper Plus steps in.
Enter Dapper Plus
A high-performance NuGet library (Z.Dapper.Plus) that adds bulk operations to IDbConnection and IDbTransaction in .NET.
Dapper Plus is designed to handle thousands or millions of entities in a single round-trip, dramatically increasing speed while maintaining flexibility.
Dapper Plus boosts your Dapper code with ultra-fast bulk operations including:
These methods are capable of processing thousands of records in milliseconds.
Benchmarks for Dapper Plus Bulk Operations
BulkInsert is faster 75x than single insert

BulkUpdate is faster 50x than single update

BulkDelete is faster 150x than single delete

BulkMerge is faster 50x than single merge

Chaining Bulk Operations in Dapper Plus
.Then() allows you to chain multiple bulk operations in one fluent call on the same connection, such as:
- BulkInsert → Then BulkUpdate
- BulkInsert → Then BulkDelete
- BulkMerge → Then BulkInsert → Then BulkUpdate
- etc.
Additional Features of Dapper Plus
In addition to the bulk extensions, Dapper Plus also includes these key features:
- Fluent Entity Mapping
- Audit
- Custom Batching
- Transactional bulk ops
- Supports wide range of Database Providers
Tutorial using Dapper Plus in ASP.NET Core
You will learn how to use Dapper Plus in an ASP.NET Core Application.
The tutorial will particularly focus on the Bulk Operations features of Dapper Plus.
The application will be a Web API that provide endpoints to bulk insert Subscribers into a Blog database, also we will provide an endpoint to retrieve the subscribers data.
We will be targeting .NET 9, the latest and greatest version of the amazing technology, with blazing-fast performance, and using the latest version of Visual Studio 2022:

Preparing the Database Server
For the sake of this tutorial, we will be using SQL Server Express, it is a lightweight version of SQL Server, that you can install on your own machine and have a database server up and running in no time.
You can easily integrate Dapper Plus with it via the IDbConnection and a connection string.
So go ahead and download SQL Server Express here, and then install SQL Server Management Studio, SSMS.
Once installed, you can connect to the server under localhost, using the below details:

Creating the Database and Table
Now that we have a local database server up and running, we can easily create a new database and add to it the Subscribers table.
So go ahead and create a new database with name ‘BlogDb’

Now, let’s run the below command to create the Subscribers table:
1 2 3 4 5 6 |
CREATE TABLE Subscribers ( Id INT IDENTITY(1,1) PRIMARY KEY, Name NVARCHAR(150) NOT NULL, Email NVARCHAR(255) NOT NULL UNIQUE, SubscribedAt DATETIME NOT NULL DEFAULT GETDATE(), ); |
Now that we have the database layer set up, let’s move on to prepare our Web API project under Visual Studio 2022.
Creating the project
Let’s start by creating a new Web API project in Visual Studio 2022.
Choose the ASP.NET Core Web API as the template, and select .NET 9 as the platform
Adding Dapper Plus
Dapper Plus is available for download under NuGet.org, so you can either download using the NuGet Package Manager GUI tool, or via the Package Manager Console, the choice is yours.
I will be downloading it using the NuGet Package Manager:

As you can also notice, we are also downloading Dapper.
We are using the Dapper library to retrieve some data from the subscribers table, but if you only want to perform write/bulk write operations, then Dapper Plus would be sufficient and it would work normally and independently of Dapper.
As you can also notice, Dapper Plus has been downloaded 5.78M+ times, it is getting a good attention because of its high value and benefit to allow performing blazing fast bulk operations.
Preparing the Models, Interfaces, Services
In this section, we will be adding the classes needed to include our code that will integrate with Dapper Plus to perform the different bulk operations along with a call to Dapper to retrieve the data.
Let’s start by creating folders: Models, Interfaces, Services
These will be used to contain the classes.
Models
Let’s create a model class that will represent the Subscriber table:
1 2 3 4 5 6 7 8 9 10 |
namespace DapperPlus.Models { public class Subscriber { public int Id { get; set; } public string Name { get; set; } public string Email { get; set; } public DateTime SubscribedAt { get; set; } } } |
Interfaces
We will have an interface that will include the main bulk functions to be performed via Dapper Plus:
Create a new item with name ‘ISubscriberService”
1 2 3 4 5 6 7 8 9 10 11 12 |
using DapperPlus.Models; namespace DapperPlus.Interfaces { public interface ISubscriberService { public Task<IEnumerable<Subscriber>> GetAllAsync(); public Task BulkInsertAsync(List<Subscriber> subscribers); public Task BulkUpdateAsync(List<Subscriber> subscribers); public Task BulkDeleteAsync(List<Subscriber> subscribers); } } |
Services
Let’s create a service with name ‘SubsriberService’:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
using System.Data; using Dapper; using DapperPlus.Interfaces; using DapperPlus.Models; using Z.Dapper.Plus; namespace DapperPlus.Services { public class SubscriberService(IDbConnection db) : ISubscriberService { public async Task<IEnumerable<Subscriber>> GetAllAsync() { // From Dapper return await db.QueryAsync<Subscriber>("SELECT * FROM Subscribers"); } public async Task BulkInsertAsync(List<Subscriber> subscribers) { await db.BulkInsertAsync(subscribers); } public async Task BulkUpdateAsync(List<Subscriber> subscribers) { await db.BulkUpdateAsync(subscribers); } public async Task BulkDeleteAsync(List<Subscriber> subscribers) { await db.BulkDeleteAsync(subscribers); } } } |
Even though we have a single class per folder, I always prefer to keep the naming of the folders plural, since most of the time, your application won’t only include a single model, interface, or service.
Writing the Controllers
Let’s now create a folder to contain our subscribers endpoint, name it ‘Controllers’
Add a controller named ‘SubscribersController’, with the below code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
using DapperPlus.Models; using DapperPlus.Services; using Microsoft.AspNetCore.Mvc; namespace DapperPlus.Controllers { [ApiController] [Route("api/[controller]")] public class SubscribersController(SubscriberService service) : ControllerBase { [HttpGet] public async Task<IActionResult> GetAll() { var subscribers = await service.GetAllAsync(); return Ok(subscribers); } [HttpPost("bulk-insert")] public async Task<IActionResult> BulkInsert(List<Subscriber> subscribers) { await service.BulkInsertAsync(subscribers); return Ok(); } [HttpPut("bulk-update")] public async Task<IActionResult> BulkUpdate(List<Subscriber> subscribers) { await service.BulkUpdateAsync(subscribers); return Ok(); } [HttpDelete("bulk-delete")] public async Task<IActionResult> BulkDelete(List<Subscriber> subscribers) { await service.BulkDeleteAsync(subscribers); return Ok(); } } } |
These are just simple methods that expose public endpoints to perform the different bulk operations along with the data retrieval for the subscribers.
As you can see, controllers should be thin and include minimal code, only a bridge to the internal components (or services).
Note: You can use Minimal Apis to achieve the same result of defining the endpoints, but I always go with Controllers, it is my personal choice, so you can feel free to go with any type.
Dapper Plus Configuration Manager
In order for Dapper Plus to bind the entity (or the model) to the associated table, we need to define a simple binding, as per the below
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
using DapperPlus.Models; using Z.Dapper.Plus; namespace DapperPlus.Config { public static class DapperPlusConfiguration { public static void Configure() { DapperPlusManager .Entity<Subscriber>() .Table("Subscribers"); } } } |
We put this inside a static method that we can easily and directly call from program.cs to configure the right mapping between the model and the table.
Wiring the Dependencies in Program.cs
Now let’s include all the dependencies and inject them through the proper means, including Dapper and Dapper Plus integrations.
Since we are connecting to SQL Server through the SqlConnection Object, it will ask you to install a dependency library ‘Microsoft.Data.SqlClient’, you can just use the VS 2022 quick action button to find and install the library for you
So here is how the Program.cs file should be:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
using DapperPlus.Config; using DapperPlus.Interfaces; using Microsoft.Data.SqlClient; using System.Data; var builder = WebApplication.CreateBuilder(args); string? connectionString = builder.Configuration.GetConnectionString("BlogDbConnectionString"); builder.Services.AddScoped<IDbConnection>(sp => new SqlConnection(connectionString)); DapperPlusConfiguration.Configure(); builder.Services.AddScoped<ISubscriberService, ISubscriberService>(); builder.Services.AddControllers(); // Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi builder.Services.AddOpenApi(); var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.MapOpenApi(); } app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run(); |
Final Folder Structure
Here is how your project folder structure should look like for this tutorial:

Running the API Locally
Let’s run our web API project locally, the default local server in ASP.NET Core projects is Kestrel – the blazing-fast lightweight cross-platform web server for ASP.NET Core.
To learn more about Kestrel, check the article in the references section.
Once you run the project, you should see the console of the Web API project:

And in the output you will see the localhost ports that your web API is hosted:

Note: Swashbuckle Swagger is no longer office supported in .NET 9, so you will no longer see the Swagger UI once you run an ASP.NET Core project, unless you add it manually from NuGet or choose another Swagger UI-based library. The choice is yours.
Testing through Postman
Now let’s test our work by performing some API calls via Postman.
Postman is an amazing tool that allows you to easily test API calls while giving you lots of features like workspaces, collections, variables, pre and post scripts and many others.
You can download Postman here.
Now open Postman, let’s add some requests to our subscribers API to test the bulk operations of Dapper Plus:
Testing Bulk Insert

And after execution, here are the data stored in the subscribers table

Testing Bulk Update


Testing Bulk Delete
With delete you can only provide the Id of each of the record, that will be enough for Dapper Plus to smartly and efficiently remove the record from the table.

And after executing this API, we can check the database table, you will no longer find the 2 records, as they were deleted via the bulk delete method of Dapper Plus.

Final Thoughts
If your application has import feature via excel sheets or other input channels, then Dapper Plus is your ultimate choice for a simple and robust solution to perform bulk operations (insert, updated, delete, and others) in the fastest possible way.
Use Dapper for day-to-day queries
Use Dapper Plus for performance-critical bulk operations
Use both when you want the best of both worlds in one project.
Try Dapper Plus today
Interesting Facts
Dapper Plus is a major sponsor and development contributor for Dapper through ZZZ Projects.
Dapper Plus can work without Dapper for all write operations (single or bulk).
References
Kestrel Server in ASP.NET Core
Bonus
Enjoy the brilliant tunes of French Baroque Music by François Couperin, who was known as Couperin le Grand (“Couperin the Great”)
The Best of François Couperin