Web API Deep Dive - DTO Transformations and Automapper (Part 5 of 6)

Microsoft’s ASP.Net Web API 2.2 allows you to easily create REST style APIs on an IIS website. Microsoft has some great documentation on how to get started with it, so I won’t rehash that here. Instead, I’m going to go a little deeper into some powerful features that can be used with Web API.

Data Transfer Objects

First, off what is this DTO thing? A Data Transfer Object is an object structure that you use to transfer data across the wire. Sometimes it will look exactly like your entity framework entities. Sometimes it will be different.

For example, suppose you have a database with Customers and Invoices, and your tables look like this:

1
2
3
4
5
6
7
8
9
10
11
12
Customer Table
--------------
CustomerId
Name
City
Telephone

Invoice Table
-------------
InvoiceId
CustomerId
Amount

These are also what your Entity models look like, because entity models reflect the database structure.

Now suppose that you want to provide an API endpoint that returns a list of customers in a particular city with the number of invoices for each customer.

The data you want to return from your API looks like this:

1
2
3
4
5
6
CustomerDTO
-----------
CustomerId
Name
Telephone
InvoiceCount

This is your DTO. You can make out where the data comes from in your database, but there is no table structure in your database that has this exact collection of fields, and thus no entity model that looks exactly like this. You will need to retrieve the data from your database and then map/transform it into this structure.

Manual Mapping

How do you map your data? You can do it manually.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Get the customers from the database
var customersFromCity = context.Customers
.Include(c => c.Invoices)
.Where(c => c.City == city);

// Count the invoices for each customer
var customersWithInvoicesCounted = customersFromCity.Select(c =>
new CustomerDTO()
{
CustomerId = c.CustomerId,
Name = c.Name
Telephone = c.Telephone
InvoiceCount = c.Invoices.Count()
}
);

This works. But it will become tedious very quickly - you will be writing many, many lines of code that do nothing more than copy a property from an entity to a DTO (and for POSTs/PUTs, you’ll do the same in reverse).

Automapper

Enter Automapper. It is designed specifically to relieve you of this tedium. It involves two steps:

1. Setup Automappper. This can be done once in a centralized location for your entire application. Here is the basic mapping setup:

1
2
3
// Customer is your Entities Customer class, and CustomerDTO is,
// well, your Customer DTO class
Mapper.CreateMap<Customer, CustomerDTO>();

Assuming that the property types and names on the two classes match, Automapper can figure out how to copy properties from one to the other. However, we have one property that can’t be directly copied - the InvoiceCount property. Thankfully, Automapper has a way to help us manually map properties. We just extend the mapping definition a little:

1
2
3
Mapper.CreateMap<Customer, CustomerDTO>()
.ForMember(dest => dest.InvoiceCount, opt =>
opt.MapFrom(src => src.Invoices.Count()));

With the ForMember() call, we’re telling Automapper that when it sees the InvoiceCount property on the DTO, it should populate the property by calling Invoices.Count() on the object it’s copying things from. The rest of the properties are figured out automatically.

2. Use Automapper to perform the mappings.

To map a single item:

1
CustomerDTO customerWithInvoicesCounted = Mapper.Map<CustomerDTO>(customer);

This is nice and simple - there is no need to manually map every single property. However, it’s slightly different when you’re dealing with a collection of items. If you have an IQueryable of customer entities, and you want to ‘preserve the IQueryable‘ if you will, Automapper can still help. Continuing with our example above, where we want to find customers from a specified city and the number of invoices they each have, you can map the data like this:

1
2
3
4
5
6
7
// Get the customers from the database
var customersFromCity = context.Customers
.Include(c => c.Invoices)
.Where(c => c.City == city);

IQueryable<CustomerDTO> customersDTO =
customersFromCity.Project().To<CustomerDTO>();

Notice what’s going on here - Automapper has just mapped your IQueryable<Customer> into an IQueryable<CustomerDTO> using the mapping instructions defined above. You can then perform a query on this IQueryable<CustomerDTO>, and the query will be executed on your SQL server. Furthermore, if OData URL Query options are turned on, you can return this IQueryable from your Web API endpoint, and the javascript client can control mapping, filtering and paging in your database even though the javascript client can only see the structure of the DTOs.

There are some limitations on what can be projected in this way - see the ‘Supported Mapping options’ section at the bottom of https://github.com/AutoMapper/AutoMapper/wiki/Queryable-Extensions for details.