HomeNikola Knezevic

In this article

Banner

Getting Started with Dapper Plus

05 Feb 2026
5 min

Special Thanks to Our Sponsors:

Sponsor Logo

EF Core is too slow? Discover how you can easily insert 14x faster (reducing saving time by 94%).

Boost your performance with our method within EF Core: Bulk Insert, update, delete and merge.

Thousands of satisfied customers have trusted our library since 2014.

👉 Learn more

Sponsor Newsletter

Dapper is fast and lightweight, but bulk operations can still be painful when you scale data volume.

Row-by-row inserts, updates, and deletes add latency and put unnecessary pressure on your database.

In today's blog post, we'll explore Dapper Plus, a high-performance library that brings bulk operations to Dapper and ADO.NET workflows without changing your mental model.

Dapper Plus

Dapper Plus is a commercial library that adds bulk insert, update, delete, merge, and synchronize to Dapper.

It executes optimized SQL directly in the database, bypassing any ORM tracking and keeping you in full control of the SQL lifecycle.

To get started with Dapper Plus, install the NuGet package. You can do this via the NuGet Package Manager or by running the following command in the Package Manager Console:

bash
Install-Package Z.Dapper.Plus

Once installed, map your entities and use extension methods directly on your IDbConnection.

For this blog post, we'll use a simple Product entity with a relationship to ProductReview:

csharp
public class Product
{
    public Guid Id { get; set; }
    public DateTime CreatedAt { get; set; }
    public DateTime? ModifiedAt { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public decimal Price { get; set; }
    public List<ProductReview> Reviews { get; set; }
}

public class ProductReview
{
    public Guid Id { get; set; }
    public Guid ProductId { get; set; }
    public Product Product { get; set; }
    public string Author { get; set; }
    public string Content { get; set; }
    public int Rating { get; set; }
    public DateTime CreatedAt { get; set; }
}

Mapping

Dapper Plus relies on explicit mapping so the library knows how to save your entities. At minimum, you need to specify the table name, but you can customize exactly which properties to save and how.

Here is a minimal mapping setup that specifies table names:

csharp
DapperPlusManager.Entity<Product>().Table("Products");
DapperPlusManager.Entity<ProductReview>().Table("ProductReviews");

But mapping can do much more. You can map specific properties, constant values, computed values, and even database-side formulas. Here's a more advanced example:

csharp
DapperPlusManager.Entity<Product>()
    .Table("Products")
    .Identity(x => x.Id)
    .Map(x => new { x.Name, x.Description, x.Price })
    .MapValue(DateTime.UtcNow, "CreatedAt")
    .MapWithOptions(x => x.ModifiedAt, options => {
        options.FormulaInsert = "now()";
    });

DapperPlusManager.Entity<ProductReview>()
    .Table("ProductReviews")
    .Identity(x => x.Id)
    .Map(x => x.Product.Id, "ProductId")
    .AutoMap();

In this example, for Product we map only specific properties, set a constant value for CreatedAt, and use a database formula for ModifiedAt. For ProductReview, we map ProductId from the parent and then call AutoMap to handle the remaining properties automatically.

NOTE: Once you start explicit mapping, Dapper Plus stops using auto-mapping logic unless you explicitly call AutoMap. This gives you full control over what gets saved.

BulkInsert

BulkInsert is the most common operation. Instead of looping inserts, it generates optimized SQL that inserts everything in a single roundtrip:

csharp
products.MapPost("/bulk-insert", (IDbConnection connection) =>
{
    var items = ProductFactory.Generate(50).ToList();

    connection.BulkInsert(items);
    return Results.Ok(new { inserted = items.Count });
});

BulkInsert doesn't track entities or run through any ORM lifecycle. You stay explicit and in control.

With Dapper Plus, you insert related data explicitly. That means you control order and keys, which is ideal when you already manage your SQL flow:

csharp
products.MapPost("/bulk-insert-related", (IDbConnection connection) =>
{
    var items = ProductFactory.Generate(10).ToList();
    connection.BulkInsert(items);

    var reviews = items
        .SelectMany(p => p.Reviews)
        .ToList();
    connection.BulkInsert(reviews);

    return Results.Ok(new { inserted = items.Count, reviews = reviews.Count });
});

This keeps everything explicit: you insert Products, then their Reviews.

BulkUpdate

BulkUpdate generates optimized SQL that updates all records in a single database operation:

csharp
products.MapPut("/bulk-update", (IDbConnection connection, List<Guid> ids) =>
{
    var items = connection.Query<Product>(
        "SELECT * FROM Products WHERE Id IN @ids",
        new { ids }).ToList();
        
    foreach (var p in items)
    {
        p.Price += 10;
        p.ModifiedAt = DateTime.UtcNow;
    }
    
    connection.BulkUpdate(items);
    return Results.Ok(new { updated = items.Count });
});

It identifies entities by their primary key and updates all modified properties.

BulkDelete

BulkDelete deletes multiple entities efficiently without loading them into an ORM change tracker:

csharp
products.MapDelete("/bulk-delete", (IDbConnection connection, List<Guid> ids) =>
{
    var items = ids
        .Select(id => new Product { Id = id })
        .ToList();
    
    connection.BulkDelete(items);
    return Results.Ok(new { deleted = items.Count });
});

BulkDelete identifies entities to delete by their primary key, so you only need to provide entities with their IDs populated.

BulkMerge

BulkMerge performs an upsert using the primary key to insert new entities and update existing ones in a single operation:

csharp
products.MapPost("/bulk-merge", (IDbConnection connection) =>
{
    var items = ProductFactory.Generate(30).ToList();
    
    connection.BulkMerge(items);
    return Results.Ok(new { merged = items.Count });
});

BulkSynchronize

BulkSynchronize goes a step further than BulkMerge by also deleting entities that exist in the database but not in your collection, ensuring your database exactly matches your in-memory collection:

csharp
products.MapPost("/bulk-synchronize", (IDbConnection connection) =>
{
    var items = ProductFactory.Generate(10).ToList();
    
    connection.BulkSynchronize(items);
    return Results.Ok(new { synchronized = items.Count });
});

Dapper Plus vs EF Extensions

Both Dapper Plus and EF Extensions are products from Z.BulkOperations and share the same bulk operation engine. The difference is how you interact with them.

Performance is the same. The difference is developer ergonomics and abstraction level. If you're already using EF Core, check out this blog post: Getting Started with Entity Framework Extensions.

Use Dapper Plus when you already write SQL, care about imports/exports or ETL, and want maximum control with minimum overhead.

Use EF Extensions when you already have an EF Core app and want bulk operations without rewriting architecture.

Conclusion

Dapper Plus delivers highly optimized bulk operations while keeping you in Dapper and ADO.NET land.

Bulk insert, update, delete, merge, and synchronize run directly in the database with minimal overhead.

Same performance as EF Extensions, different abstraction level.

Although it’s a commercial library, the speed and productivity gains often justify the investment.

If you want to check out examples I created, you can find the source code here:

Source Code

I hope you enjoyed it, subscribe and get a notification when a new blog is up!

Subscribe

Stay tuned for valuable insights every Thursday morning.