Introduction to Dapper - A Lightweight ORM for C#
From ADO.NET to Object Mapping
You should already be familiar with using ADO.NET for accessing a database. If not, have a look at Introduction to Databases in CSharp.
We saw that while ADO.NET gives full control, it also requires a lot of repetitive code:
- Opening and closing connections
- Creating commands and parameters
- Reading rows from
DataReader - Manually converting rows into C# objects
This process is known as object mapping, and doing it manually can be time-consuming and error-prone.
What Is an ORM?
ORM stands for Object-Relational Mapper.
It’s a programming technique (and a class of libraries) that automatically maps database tables to C# classes and SQL results to objects.
An ORM handles:
- Converting objects → database rows (inserts,updates)
- Converting database rows → objects (queries)
- Managing SQL parameters automatically
With an ORM, developers can focus more on business logic instead of the mechanics of SQL data handling.
Dapper - A Lightweight ORM
Dapper is a micro ORM - meaning it adds just enough abstraction to simplify data access, but still allows you to write your own SQL.
- It’s developed by the Stack Overflow team.
- It builds on top of ADO.NET.
- It provides extension methods for
IDbConnectionsuch asQuery,QueryFirst,Execute, etc.
Advantages
- Very fast and lightweight.
- Minimal setup and dependencies.
- Keeps SQL control (no “magic” query generation).
Disadvantages
- No automatic schema migrations or relationship tracking.
- You still write SQL manually.
Setting Up Dapper
Install Dapper from NuGet:
dotnet add package Dapper
dotnet add package MySql.DataImport the namespaces:
using Dapper;
using MySql.Data.MySqlClient;Using Dapper for CRUD Operations
Let’s assume we have a Student class and a MySQL table named
Students:
CREATE TABLE Students (
Id int PRIMARY KEY AUTO_INCREMENT,
Name VARCHAR(100),
Email VARCHAR(100)
);public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}1. Create (Insert)
using var connection = new MySqlConnection(_connectionString);
string sql = "INSERT INTO Students (Id, Name, Email) VALUES (@Id, @Name, @Email)";
var student = new Student { Name = "Alice", Email = "alice@example.com" };
connection.Execute(sql, student);
Console.WriteLine("Student inserted successfully.");🧩 What happens here:
Dapper looks at the student object, reads its property values, and
automatically binds them to the SQL parameters.
2. Read (Select by Id)
string sql = "SELECT * FROM Students WHERE Id = @Id";
var student = connection.QueryFirstOrDefault<Student>(sql, new { Id = 42 });
if (student != null)
Console.WriteLine($"{student.Name} - {student.Email}");
else
Console.WriteLine("Student not found.");🧩 Explanation:
QueryFirstOrDefault<T> executes the SQL and automatically maps the
result row to a Student object.
3. Read All
string sql = "SELECT * FROM Students";
var students = connection.Query<Student>(sql).ToList();
foreach (var s in students)
Console.WriteLine($"{s.Id}: {s.Name} ({s.Email})");🧩 Explanation:
Query<T> automatically reads all rows and returns an IEnumerable<T> - each row is converted into a Student object.
4. Update
string sql = "UPDATE Students SET Name = @Name, Email = @Email WHERE Id = @Id";
var student = new Student
{
Id = 42,
Name = "Alice Updated",
Email = "alice.updated@example.com"
};
connection.Execute(sql, student);
Console.WriteLine("Student updated successfully.");🧩 Explanation:
Execute runs a SQL command that does not return results (INSERT, UPDATE, DELETE).
The student object automatically provides the parameter values.
5. Delete
string sql = "DELETE FROM Students WHERE Id = @Id";
connection.Execute(sql, new { Id = "some-id" });
Console.WriteLine("Student deleted.");🧩 Explanation:
Here we pass an anonymous object (new { Id = ... }) instead of a full entity.
Dapper maps this parameter directly to the SQL placeholder.
Key Takeaways
- Dapper is a middle ground between raw ADO.NET and full ORM frameworks.
- You still write SQL, but Dapper handles object mapping and parameter binding.
- It’s ideal for applications that want simplicity and performance without giving up control over queries.