Skip to main content
ANVISoftware Solutions
Lesson 1 of 17Intermediate13 min

What Is an ORM?

By the end of this lesson

Explain what an ORM does for you, and what it deliberately hides.

Your application thinks in objects: an Employee with a name and a salary. Your database thinks in rows and columns. An object-relational mapper sits between them and translates.

Without one, you write SQL by hand, read each column out of a result, and construct objects yourself. It works, and it is a great deal of repetitive code that has to change every time a column does.

Without an ORM — the plumbing is yours
C#
List<Employee> employees = new();

using SqlConnection connection = new(connectionString);
await connection.OpenAsync();

using SqlCommand command = new(
    "SELECT Id, Name, Role, AnnualSalary FROM Employees WHERE Role = @role",
    connection);
command.Parameters.AddWithValue("@role", "Engineer");

using SqlDataReader reader = await command.ExecuteReaderAsync();

while (await reader.ReadAsync())
{
    employees.Add(new Employee
    {
        Id = reader.GetInt32(0),
        Name = reader.GetString(1),
        Role = reader.GetString(2),
        AnnualSalary = reader.GetDecimal(3)
    });
}
  • Correct, and entirely mechanical. Note that the column positions 0 to 3 are tied to the order in the SELECT — reorder the query and this breaks silently.
  • Every table needs its own version of this, and every schema change means revisiting it.
With EF Core — the same result
C#
List<Employee> employees = await context.Employees
    .Where(e => e.Role == "Engineer")
    .ToListAsync();
  • EF Core generates the SQL, runs it, and builds the objects.
  • The query is written in C#, so renaming the Role property updates this automatically and misspelling it fails to compile rather than at run time.

What you get:

Less repetitive code
Reading and writing rows stops being something you hand-write per table.
Type-checked queries
Query mistakes surface at compile time instead of as a run-time SQL error.
Schema under version control
Migrations record every schema change alongside the code that needs it.
Change tracking
Modify a loaded object, call save, and EF Core works out the UPDATE for you.
What happens when an ORM query runsA LINQ query in your application is translated by the ORM into SQL. The SQL is sent over a connection to the database, which executes it and returns rows. The ORM then materialises those rows back into objects. The network round trip is usually the most expensive stage, which is why the number of queries matters more than their individual complexity.LINQ querywritten in C#EF CoretranslatesSQLDatabaseRowsMaterialiserows to objectsYour objectsThe round trip between application and database is the expensive part.One query returning 100 rows is far cheaper than 100 queries returning one row each.That second case is the N+1 problem, and it is the most common ORM performance bug.
What happens when you run a query

The diagram shows why the abstraction leaks. Your LINQ query is translated into SQL, sent over a network connection, executed by the database, and the results are materialised back into objects. Each of those stages has a cost, and the round trip is usually the expensive one.

This is why a query returning ten thousand rows to count them is slower than asking the database for the count — and why knowing what SQL you generated is not optional detail.

Summary

  • An ORM translates between objects in your code and rows in your database
  • It removes repetitive mapping code and makes queries type-checked
  • It hides the SQL, which is both the point and the main risk
  • Use it for ordinary data access; drop to SQL deliberately for reports and bulk work

Practice

Attempt each one before opening the solution. Getting it wrong first is how the idea sticks.

Think about it

Think about it

In the hand-written example, the column indexes 0 to 3 correspond to the order of columns in the SELECT. What happens if someone adds a column to the middle of that SELECT list, and why is this class of bug particularly unpleasant?

Show solution

Every index after the insertion point now refers to the wrong column. If the types happen to line up, it compiles and runs, and silently populates fields with the wrong data.

It is unpleasant precisely because it does not throw. A role might end up in a name field and nothing reports an error. An ORM removes this entire category of mistake by mapping on names rather than positions.

Knowledge check

Nothing is recorded and there is no score. The explanation appears either way.

What is the main risk of using an ORM?

Saved in this browser only.