2

I am looking for an explanation of the difference between CreateMap/CreateProjection in automapper and ProjectTo/MapTo by relation. I just got started with the library and I am having trouble understanding what to use when. I partially understand that ProjectTo has some relation to LINQ and can be used for entire collections? I wish to use this library in a Blazor Server Side project.

I am also looking into these two libraries as I am using EF Core:

But as I am new to the library I think it is a good idea to start with the base first before moving on.

Luk164
  • 657
  • 8
  • 22

2 Answers2

8

TL;DR

If you don’t use Map, just ProjectTo, you should use CreateProjection instead of CreateMap. That way you’ll use only the API subset supported by ProjectTo and start-up should be faster.

CreateProjection explicitly disables Map. CreateMap allows both.

So if you need only entity -> DTO mapping via ORM (like Entity Framework) and ProjectTo then use CreateProjection, otherwise use CreateMap.

Details

As written in the docs if you are working with ORM based on IQueryable you can use ProjectTo so the AutoMapper+ORM pair can generate a proper SQL statement instead of fetching the whole entity into memory (if your are not mapping in one-to-one fashion this can have positive effect on performance) and mapping it client side:

var configuration = new MapperConfiguration(cfg =>
    cfg.CreateProjection<OrderLine, OrderLineDTO>()
    .ForMember(dto => dto.Item, conf => conf.MapFrom(ol => ol.Item.Name)));

public List<OrderLineDTO> GetLinesForOrder(int orderId)
{
  using (var context = new orderEntities())
  {
    return context.OrderLines.Where(ol => ol.OrderId == orderId)
             .ProjectTo<OrderLineDTO>(configuration).ToList();
  }
}

The .ProjectTo<OrderLineDTO>() will tell AutoMapper’s mapping engine to emit a select clause to the IQueryable that will inform entity framework that it only needs to query the Name column of the Item table, same as if you manually projected your IQueryable to an OrderLineDTO with a Select clause.

Due to the nature of the ProjectTo it is much more limited in what it can actually map/do (not everything can be turned into SQL).

Read more:

  1. What is the difference between IQueryable and IEnumerable?
  2. Defining both CreateProjection and CreateMap

    CreateProjection explicitly disables Map. CreateMap allows both.

  3. 11.0 Upgrade Guide:

    If you don’t use Map, just ProjectTo, you should use CreateProjection instead of CreateMap. That way you’ll use only the API subset supported by ProjectTo and start-up should be faster.

  4. 12.0 Upgrade Guide

    You also cannot have for the same map/member separate configurations for Map and ProjectTo.

  5. AutoMapper ConvertUsing is not called
Guru Stron
  • 102,774
  • 10
  • 95
  • 132
2

The short answer is it's generally better to use CreateMap.

Create map defines mappings that can be used for both MapTo and ProjectTo whereas CreateProjection defines mappings that only works with ProjectTo and effectively disables the use of MapTo.

Using CreateMap will give you the freedom to map in-memory objects and database queries (that work with IQueryable) directly into your chosen object.

CreateProjection can be used if you will only be mapping straight from DB queries

YungDeiza
  • 3,128
  • 1
  • 7
  • 32
  • Allright, but what does it mean when I need to go the other way around? Say I fetched the data I needed using ProjectTo and made some edits to the DTO, how do I save the changes now? The CreateProjection does not seem to have a .Reverse() so do I need to create two? – Luk164 Feb 16 '23 at 10:12
  • 3
    @Luk164 you can use `CreateProjection` for entity -> DTO and `Map` for DTO -> entity. Personally I prefer to avoid `Reverse` unless it is used for simple one-to-one mappings without any customization. – Guru Stron Feb 16 '23 at 10:17
  • @GuruStron Thx. Right now my DTOs are mostly just 1:1 copies with the backwards navigation parameters removed so that my DataGrid does not do stupid stuff. I did not really perform any flattening yet and I do not really see the point in BlazorSS – Luk164 Feb 16 '23 at 10:20
  • @GuruStron BTW what happens if I have both Map and Projection defined for the same type? – Luk164 Feb 16 '23 at 10:22
  • 1
    @GuruStron Correct but I think `Reverse` is fine - you just need to make sure that any mapping rules that's you've setup on in the initial definition also have a corresponding definition for the reverse. – YungDeiza Feb 16 '23 at 10:23
  • 2
    @Luk164 you won't be able to use `MapTo` if `CreateProjection` is used. – YungDeiza Feb 16 '23 at 10:24
  • @YungDeiza Yeah but what hapens if I have BOTH defined? Does projection get used first and map as a fallback? BTW how do I define a reverse mapping rule? – Luk164 Feb 16 '23 at 10:25
  • 3
    @Luk164 I have not tried it myself but as written in the upgrade to 12 version - _"You also cannot have for the same map/member separate configurations for Map and ProjectTo."_ (this is covered in my answer too). – Guru Stron Feb 16 '23 at 10:29
  • 1
    @GuruStron Correct. It will compile though but you'll get a runtime exception. – YungDeiza Feb 16 '23 at 10:32
  • 2
    @YungDeiza _I think Reverse is fine_ - it's just a personal preference after having shot myself on the foot several times with it over the years =) – Guru Stron Feb 16 '23 at 10:32
  • 2
    @YungDeiza yep, runtime exception was my guess =) – Guru Stron Feb 16 '23 at 10:33
  • 1
    @GuruStron fair enough, I can see that happening. – YungDeiza Feb 16 '23 at 10:33
  • @GuruStron I am having trouble understanding the point of CreateProjection in that case. Wouldn't that just make it very hard to save any edited objects? From what you said it sounds like a one way street to me as I cannot have a mapping to go back to original DOM object. – Luk164 Feb 16 '23 at 10:33
  • @GuruStron Or do I just need to do a CreateProjection and CreateMap? – Luk164 Feb 16 '23 at 10:36
  • 2
    @Luk164 _"Wouldn't that just make it very hard to save any edited objects"_ - why? You just use `CreateMap` for reverse direction (DTO -> entity). As far as I understand main point of `CreateProjection` is to improve startup performance (doubt that it will be that noticable in general case though) and limit the API used so reduce number of potential errors at runtime. – Guru Stron Feb 16 '23 at 10:36
  • @Luk164 yes, exactly. – Guru Stron Feb 16 '23 at 10:36
  • 1
    @GuruStron Ok, it is all starting to make sense to me now. Thank you both – Luk164 Feb 16 '23 at 10:38