I have been trying to map my domain objects to a report view model. Things all worked well in testing where I faked the entity framework code out and used a builder to return a fully populated pocco object. Now that I am actually hitting the database and returning data I am seeing some wierd dynamic proxy type errors.
Here is a sample of my code:
public class ContactMapping : Profile
{
protected override void Configure()
{
Mapper.CreateMap<Contact, ReportRowModel>()
.ForMember(dest => dest.Gender, opt => opt.MapFrom(src => src.Gender.Name));
}
}
And the mapping code is like this:
var contact = GetContactFor(clientPolicy);
Mapper.DynamicMap(contact, rowModel);
return rowModel;
The contact fields all populate correctly except for the rowModel.Gender field which is returning System.Data.Entity.DynamicProxies.Gender_3419AAE86B58120AA2983DA212CFFEC4E42296DA14DE0836B3E25D7C6252EF18
I have seen solutions where people have had problems using Map instead of DynamicMap, but I haven't found anything where a .ForMember mapping is failing like this.
Any suggestions.
Your EF query is not returning the Gender, it is returning a Proxy that can get Gender for you when evaluated, which is not of the type that AutoMapper built a mapping to handle.
You either need to eagerly fetch Gender in your query, or use AutoMapper's IQueryable Extention's Project method to have AutoMapper emit an anonymous projection (again, in your query), rather than try to apply the AutoMapping after the result has been returned from your EF context.
This is good practice in general to avoid Select N+1 issues.
I've got the same issue right now with version 4.x, reverting to 3.3.1 fixed the issue.
Related
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:
https://github.com/AutoMapper/AutoMapper.Collection
https://github.com/AutoMapper/AutoMapper.Collection.EFCore
But as I am new to the library I think it is a good idea to start with the base first before moving on.
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:
What is the difference between IQueryable and IEnumerable?
Defining both CreateProjection and CreateMap
CreateProjection explicitly disables Map. CreateMap allows both.
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.
12.0 Upgrade Guide
You also cannot have for the same map/member separate configurations for Map and ProjectTo.
AutoMapper ConvertUsing is not called
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
I have issues with DTO mapping. I'm using OData WebAPI and I need to use IQueryable due to paging, sorting, filtering...
When I use this code (simplified for this purpose) in my WebAPI controller, it works
return Ok(_dataService.GetEntities(idUser).Select(e => new EntityDTO
{
ID = e.ID,
Name = e.Name
}));
but when I have separate method for DTO mapping it does not work.
return Ok(_dataService.GetEntities(idUser).Select(e => _dataService.MapToEntityDTO(e)));
Methods in my _dataService object (simplified)
public IQueryable<Entity> GetEntities(long idUser)
{
return from z in _context.Entities select z;
}
public EntityDTO MapToEntityDTO(Entity entity) {
return new EntityDTO {
ID = entity.ID,
Name = entity.Name
};
}
Could you please someone explain me what is wrong with that ? Thanks for help.
It would appear that GetEntities is returning an IQueryable that EF would be deferring execution until the results need to be materialized. This would fail if you try calling some arbitrary C# method (MapToEntity) because EF cannot translate that down into SQL.
What you should consider using is Automapper which contains a method called ProjectTo which can integrate with EF's IQueryable to project your DTOs through a configured mapper.
var dtos = _dataService.GetEntities(idUser)
.ProjectTo<EntityDTO>(config)
.ToList();
return Ok(dtos);
Where "config" is an instance of the Automapper Configuration class containing the mapping config to convert the Entity(ies) to DTO(s).
The alternative with a custom mapper that doesn't integrate with IQueryable is that you would have to materialize the entities first, then perform the mapping in memory:
var dtos = _dataService.GetEntities(idUser)
.ToList() // Materializes the entities
.Select(e => _dataService.MapToEntityDTO(e))
.ToList();
return Ok(dtos);
The disadvantages of this approach are that it requires more memory and time from the server to load the entities into memory first, then perform the mapping Select. This also requires that the GetEntities() method ensures that any/all related entities that might be mapped are eager loaded, otherwise they would either trigger lazy loads or be left #null. This can blow out time and memory usage where little of this related data might actually be needed.
With the ProjectTo projection, the queries will automatically fetch whatever related details are needed without the overhead of needing to eager load relations or tripping lazy loads.
I'm writing a few simple extension methods to create DTOs out of entities defined in the domain, however, those entities have properties that are also entities, and I'd like to be able to write something (that I personally find elegant) like they do in EF Core with Include().ThenInclude().
Ideally I'd like to be able to write something like
return myEntity.ToDto().Include(entity => entity.SubEntity).ThenInclude(subEntity => subEntity.AnotherSubEntity);
Is it possible?
The idea is that if I just call ToDto() I would simply receive a basic DTO object where all simple type properties are set but all complex type properties are null, unless I specify that I want to inclue one (or more) of the properties too.
AutoMapper Queryable Extensions provides a convenient way to query entities with relationships and map them to DTOs in one step.
It produces an optimized SQL query needed to copy that data.
Example:
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<BlogDTO, Blog>().ReverseMap();
cfg.CreateMap<PostDTO, Post>().ReverseMap();
});
return _context.Blogs.Where(b => b.Id == Id)
.Include(x => x.Posts)
.ProjectTo<BlogDTO>(config)
.ToList();
I've actually managed to do it by sort of copying how EF Core does it with a lot of reflection and expression trees, and by creating my own visitor to then process the whole expression tree.
I am now able to write code like: myEntity.AsDtoable().Include( x => x.ComplexTypeProp ).ThenInclude( x => x.AnotherComplexTypeProp ).ToDto< EntityDto >();
We recently implemented automapper and currently looking to optimise assigning values from the DTO to model. currently we are doing something like
model.Property1 = dto.Property1;
model.SomePropertyType = dto.PropertyType;
model.Property2 = dto.Property2;
Now this could go pretty long and repetitive task to all Mapper classes.
Is there a way to simplify this on AutoMapper?
If you are using Automapper then have you not defined the maps (profile)? I believe, you might have defined those so please use those to instruct Automapper how to map source object to Target.
Another point Automapper also works based on naming convention, so if you have same property name in both source and target then it will automap automatically. So you don't unnecessary define the mapping fort it. To override mapping for a property (or whose name/type does not match), you can use .ForMember method.
cfg.CreateMap<MyDTO, MyModel>()
.ForMember(destination => destination.PropertyType,
opts => opts.MapFrom(source => source.SomePropertyType ));
You can read about Automapper at here.
Now in the code to get the mapped object, use it like
Mapper.Map<MyModel>(object of MyDTO);
I'm beginning AutoMapper and had a question. I came across sample code like this:
Automapper.Mapper.CreateMap<Book, BookViewModel>()
.ForMember(dest => dest.Author,
opts => opts.MapFrom(src => src.Author.Name));
So this would let me convert a book to a book model, and then map the src to the src.author.name property because there was not a 1-1 mapping.
To confirm, it does not work in reverse on its own, meaning I now need to explicitly do this:
Mapper.Mapper.CreateMap<BookViewModel, Book>()
.ForMember(dest => dest.Author.Name,
opts => opts.MapFrom(src => src.Author));
Is that correct? So to further confirm, if I had 50 view models and views, I'd literally need 100 (one for the way in, one for the way out, plus any additional lines of code in the .ForMember expression)
Is this true? Is this a common practice (i.e. you could potentially see hundreds of lines of code to handle the field mapping back and forth for multiple DTOs with properties that do not match up 1-1)?
CreateMap creates a mapping for AutoMapper to use later when required. This is required only once in the lifetime of your Application. In order to make use of this mapping you have defined, you just need to call Automapper.Map method. Thus you don't have to create the mapping again and again.
Example:
var myBookViewModel = Automapper.Map<Book, BookViewModel>(myBook);
You are correct saying that defining a mapper for Book to BookViewModel will not make Automapper to create a mapper for BookViewModel to Book automatically for you. You have to create the mapper.
In essence yes, you'd need basically to specify both back and forth configurations to handle each DTO mapping. Without a deterministic pattern it is impossible to guess how the mapping would work between complex object when members' names are different, or when the properties you want are further within other objects, like in your example.
However, Automapper is fairly smart if you follow some basic patterns. In your case, if you change the DTO property name to AuthorName instead of Author, the CreateMap will properly map without having to specify the mapping manually (they call this "Flattening"). Automapper will even map methods to properties if they follow a GetProperty() => Property { get; set; } pattern. Renaming your DTO properties to follow this pattern could probably save you a bunch of lines. Take a look at their starters guide for other ways to simplify your mappings.