Pebellslearn by building
0%
RoadmapsMatrixCoursesSandboxSign in
Object Relational Mapping  ›  Lesson

ORMs and Dapper — Rows In, Objects Out

Dapper 14 minAn ORM maps database rows to C# objects; a micro-ORM like Dapper maps the results of SQL you write, while a full ORM also writes the SQL and tracks changes.

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.

Three ReadingRoom queries with DapperStep 1 of 4
  1. All books after 2000

    Many rows expected, so QueryAsync<Book>. It returns an IEnumerable<Book>.

The trap: $"... WHERE Title = '{title}'". A title such as x' OR '1'='1 rewrites the query.

Drills

Answer fast — trust the rule.

quick drill

What does Dapper NOT do for you?

quick drill

Which Dapper method fits DELETE FROM Loans WHERE Id = @Id?

quick drill

Why pass new { Id = id } instead of building the SQL with the id inside it?

quick drill

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.

Practice — level up

0/1 solved

Write your answer, then compare it with a rubric and a model answer.

ExplainDapper or a full ORM?

Show how ReadingRoom would fetch one book by id with Dapper, explain why the id must be passed as a parameter rather than pasted into the SQL string, and say when you would choose Dapper over Entity Framework Core.