OData Web APIs with AutoMapper 3

When developing an API on top of a domain layer, we rarely want to expose the actual domain objects to the API consumers. Rather, it is usually a matter of presenting the consumers with a subset of the domain object’s properties or a DTO/model object representing a composite of multiple domain objects.

Although the combination of OData and Entity Framework does provide some control of the presentation of the objects returned, it quickly falls apart when more advanced combinations and composites are needed. This meant that, to me, OData via .NET Web Api was not a viable alternative in most of my real world (read: customer) projects.

Enter AutoMapper. It has been an invaluable part of my development arsenal for quite some time, and the introduction of LINQ functionality in its latest incarnation makes an already awesome library even better. The LINQ support means that AutoMapper no longer populates the source objects completely, skipping any properties which aren’t needed for the mapping to the destination type. In other words, sensibly designed DTOs and some careful mapping configuration is all that’s needed to create an effective OData API.

Example

Given the domain object below:

[gist https://gist.github.com/JoachimL/6387670 /]

An ordinary domain object, containing a couple of properties we are not likely to want to expose over an OData API. It would be a horrible idea to expose the Image byte array, and there’s no need to expose the user who added the movie to the database, either. For this examples’s sake, we will limit the OData presentation of a movie to its Id, Title and Year Of Release.

[gist https://gist.github.com/JoachimL/6387685 /]

The mapping profile needs to be configured, and that’s typically done in a separate class inheriting from AutoMapper.Profile. AutoMapper does a good job of matching and mapping properties which share names, but has to be told that we just want the year part of the release date: [gist https://gist.github.com/JoachimL/6387662 /]

When that’s done, we merely need to set up an OData controller to delivery Movie objects over OData, and create a service and repository to bring the objects from the database to the controller. The Service, in this example called «MovieService», fetches the domain objects from an instance of the MovieRepository class. This layering might seem a bit contrived in our simple example, but it should prove that this technique is viable in a real world multi-layered architecture.

[gist https://gist.github.com/JoachimL/6387700 /] [gist https://gist.github.com/JoachimL/6387705 /]

I will skip the configuration of the actual OData endpoint, but I have more or less copied it verbatim from http://msdn.microsoft.com/en-us/magazine/dn201742.aspx. In addition, a visual studio 2012 project containing a runnable version of the code in this article is available on GitHub: https://github.com/JoachimL/WebApi.OData.

The most interesting part of this example, at least for those of us familiar with AutoMapper v2, is this line in the service: [gist https://gist.github.com/JoachimL/6387847 /]
In version 2, it would most likely look like this instead: [gist https://gist.github.com/JoachimL/6387854 /]

The difference is the Project().To()-syntax, which ensures that only the property values needed for the mapping are retrieved from the domain objects (and, by extension, the database), The OData endpoint now returns the Title and the Year of Release:

List of all the movies in the database
All movies in the database

SQL Express Profiler proves that the LINQ/AutoMapper/Entity Framework combination leads to an SQL query fetching just the properties needed:

LINQ-generated SQL.
LINQ-generated SQL.

If we try to fetch all Movies starting with a “D”, this is what happens:

movies_whose_filter_start_with_d

And the SQL generated to fetch the objects is still as slim as possible.

How about adding elements to the Movie model which aren’t part of the Movie domain object? No problem, we’ll first extend the Model:

[gist https://gist.github.com/JoachimL/6387670 /]

And then give AutoMapper a hand setting up the properties it’s not able to figure out itself:

[gist https://gist.github.com/JoachimL/6387662 /]

This leads to more data being returned when getting the same URL as previously:

movies_d_with_actors_and_ratings

And even though the SQL’s complexity increases, it’s still limited to the properties/fields it actually needs:

movies_d_with_actors_and_ratings_sql

Hopefully, these examples will give you an idea of how the combination of Entity Framework (and other ORMs) and AutoMapper is a powerful and productive combination. If OData fits the use case, devs can focus on the throttling and security side of things, and let the API consumers themselves decide how they search.

There are some security issues which must be addressed in a production system which I haven’t covered in this article. There is an abundance of advice and tutorials on that topic all around the internet. http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/odata-security-guidance is a good starting point.

I have not yet had the time to experiment with how the OData $expand option can be used within the context of EF/LINQ and AutoMapper.

References:

https://github.com/AutoMapper/AutoMapper/wiki

https://github.com/AutoMapper/AutoMapper/wiki/Queryable-Extensions

http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api

http://expressprofiler.codeplex.com/

* For a discussion on the topic, see http://stackoverflow.com/questions/16962081/asp-net-webapi-odata-support-for-dtos.