ORMs and Dapper — Rows In, Objects Out
A database returns rows; your C# code wants Book objects. Copying each column into each property by hand, for every query, is tedious and error-prone. Object-relational mappers (ORMs) do the copying. How much more they do is the real choice.
The rule
Micro-ORM: you write the SQL, it maps the rows. Full ORM: it writes both.
.NET's ORMs sit on a scale:
- Dapper — a micro-ORM. You write every SQL statement; it runs it with parameters and maps the result columns to properties by name. Tiny and very fast.
- RepoDB — a hybrid: Dapper-style raw SQL plus built-in helpers for common operations.
- Entity Framework Core — Microsoft's full ORM. You write LINQ in C#; it generates SQL, tracks changes to objects and manages the schema with migrations.
- NHibernate — an older, mature full ORM, found in many long-lived codebases.
Dapper adds extension methods to any DbConnection (it opens a closed one for you):
public record Book(int Id, string Title, string Author, int Year);
await using var connection = new SqlConnection(connectionString);
var book = await connection.QuerySingleOrDefaultAsync<Book>(
"SELECT Id, Title, Author, Year FROM Books WHERE Id = @Id",
new { Id = id });
var recent = await connection.QueryAsync<Book>(
"SELECT Id, Title, Author, Year FROM Books WHERE Year >= @From",
new { From = 2000 });
await connection.ExecuteAsync(
"UPDATE Loans SET ReturnedAt = @Now WHERE Id = @LoanId",
new { Now = DateTime.UtcNow, LoanId = 40 });The anonymous object supplies parameters: @Id is filled from Id. They travel separately from the SQL text, so a value can never change the query. Building SQL by pasting input into a string invites SQL injection.
The shortcut
Match the method name to the shape of the answer: many rows → QueryAsync; one row or none → QuerySingleOrDefaultAsync; no rows, just a change → ExecuteAsync.
All books after 2000
Many rows expected, so
QueryAsync<Book>. It returns anIEnumerable<Book>.
The trap: $"... WHERE Title = '{title}'". A title such as x' OR '1'='1 rewrites the query.
Drills
Answer fast — trust the rule.
What does Dapper NOT do for you?
Which Dapper method fits DELETE FROM Loans WHERE Id = @Id?
Why pass new { Id = id } instead of building the SQL with the id inside it?
Which of these is a full ORM that generates SQL from LINQ?
Remember
- Micro-ORM: you write the SQL, it maps the rows. Full ORM: it writes both.
- Many rows →
QueryAsync; one or none →QuerySingleOrDefaultAsync; changes →ExecuteAsync. - Always pass values as parameters, never pasted into SQL.
