-
Notifications
You must be signed in to change notification settings - Fork 0
Home
When building an API, you'll encounter DTOs — also named API models. When using Entity Framework, you'll have to project your database models into your DTOs. This should look like something similar to this:
var people = await _dbContext.Select(x => new PersonDto
{
Id = x.Id,
FirstName = x.FirstName,
LastName = x.LastName
}).ToListAsync();
Then, a problem arises: how to reuse those expressions? The most obvious way to do that is to create a static readonly field with Expression<Person, PersonDto>
. But, we then hit a roadblock: with EF's IQueryable<T>
, expressions are not reusable inside other expressions. Basically, the following will throw:
var people = await _dbContext.Select(x => new PersonDto
{
Id = x.Id,
FirstName = x.FirstName,
LastName = x.LastName,
FavoriteBooks = x.FavoriteBooks.Select(BookDto.MappingExpression).ToList() // Doesn't work!!!
}).ToListAsync();
NotSoAutoMapper fixes this problem by wrapping expressions inside a Mapper<TSource, TResult>
and transforming the expressions by inlining any usage of the mapper. Little bonus: you can also write custom methods that get transformed into another expression, very useful for reusing some code inside IQueryable
s!
Make sure to install the NotSoAutoMapper
package from NuGet!
Let's assume that we have the following classes:
public class Person
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public long SocialSecurityNumber { get; set; }
}
public class PersonDto
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
To map a Person
to a PersonDto
, we can use Mapper<Person, PersonDto>
. Let's add it to the PersonDto
class.
public class PersonDto
{
// ... <Previous properties>
public static readonly Mapper<Person, PersonDto> Mapper = new(x => new PersonDto
{
Id = x.Id,
FirstName = x.FirstName,
LastName = x.LastName
});
}
That's it! The code is pretty straight forward, we're declaring a Mapper
which is simply mapping all the properties from Person
to PersonDto
. Writing this code manually is a bit tedious, fortunately the Visual Studio extension MappingGenerator is there to generate this boilerplate code for us.
There are many ways of using mappers. They are all usable inside Mapper
expressions, and outside of them as well. In order to use mappers inside IQueryable<T>
expressions, you need to use them inside a Mapper
, because expressions need to be transformed to inline the mappers' expressions.
Use element.MapWith(mapper)
or mapper.Map(element)
.
new Mapper<Book, BookDto>(b => new BookDto
{
Id = b.Id,
Name = b.Name,
Author = b.Author.MapWith(PersonDto.Mapper)
});
var personDto = person.MapWith(PersonDto.Mapper);
Use enumerable.MapWith(mapper)
. This can also safely be used with IQueryable<T>
.
new Mapper<Team, TeamDto>(b => new TeamDto
{
Id = b.Id,
Name = b.Name,
Members = b.Members.MapWith(PersonDto.Mapper)
});
var teamDtos = await _context.Teams.MapWith(TeamDto.Mapper).ToListAsync();