Roadmap › 1. .NET › Step 16
Dp
Dapper — The Micro-ORM from Basics to Advanced
What Dapper is and how it works inside, Query/QueryFirst/QuerySingle/Execute, parameters and SQL injection safety, stored procedures and output parameters, multi-mapping joins, QueryMultiple, transactions, bulk inserts and table-valued parameters, async and buffering, custom type handlers, DynamicParameters, repository and unit-of-work patterns, Dapper vs EF Core vs ADO.NET, performance and testing.
What it is & where it is used
Dapper is a lightweight object mapper ("micro-ORM") created by Stack Overflow (Sam Saffron and Marc Gravell, 2011). It adds extension methods to IDbConnection that run your SQL and map the results to objects — nearly as fast as hand-written ADO.NET, with a fraction of the code.
| ADO.NET | Dapper | EF Core | |
|---|---|---|---|
| You write SQL | Yes | Yes | No (LINQ → SQL), optional raw SQL |
| Mapping rows to objects | Manual (reader["Name"]) | Automatic by column name | Automatic |
| Change tracking, migrations, relationships | No | No | Yes |
| Performance | Fastest (manual) | ~Same as ADO.NET | Good, more overhead |
| Code volume | High | Low | Lowest for CRUD |
| Best for | Special cases, bulk | Reports, read-heavy APIs, stored procedures, performance-critical paths | Domain models, CRUD, complex writes |
Many teams combine them: EF Core for writes, Dapper for complex reads/reports (CQRS-lite).
Versions & history
| Year | Milestone |
|---|---|
| 2011 | Dapper open-sourced by Stack Overflow |
| 2012–2016 | Multi-mapping, QueryMultiple, async methods, .NET Core support |
| 2017 | Dapper 1.50+ for .NET Standard |
| 2019 | Dapper 2.0 — modern target frameworks |
| 2023 | Dapper 2.1 — AOT-friendlier work begins; Dapper.AOT (source-generated, no runtime reflection emit) |
| 2024–2025 | Dapper 2.1.x maintained; works with Microsoft.Data.SqlClient, Npgsql, MySqlConnector, SQLite |
Related packages: Dapper.Contrib / Dapper.SimpleCRUD (generated CRUD), Dapper.AOT, DapperQueryBuilder, Dapper.Bulk.
Lifecycle
What happens when you call connection.Query<Order>(sql, new { CustomerId = 42 }):
- Dapper builds a cache key from the SQL text, the parameter object's type and the result type.
- First time only: it generates (via IL emit / compiled code) a parameter writer that turns the anonymous object's properties into
DbParameters, and a row mapper that reads aDbDataReaderrow into anOrderby matching column names to properties/constructor parameters. These are cached — later calls are fast. - Opens the connection if it is closed (and closes it again afterwards).
- Creates a
DbCommand, adds parameters, executesExecuteReader. - Maps each row with the cached mapper; with
buffered: true(default) it reads all rows into aList<T>before returning. - Disposes the reader and command.
Tools used to develop
- NuGet:
Dapper,Microsoft.Data.SqlClient(SQL Server),Npgsql,MySqlConnector,Microsoft.Data.Sqlite. - SQL Server Management Studio / Azure Data Studio for writing and tuning SQL; actual execution plans.
- MiniProfiler (also from Stack Overflow) to see SQL timings per request.
- Testcontainers (real SQL Server in Docker) for integration tests.