I am trying to use AutoMapper 6.3 to allow me to auto map my model into viewmodels.
First I registered my AutoMapper instance to my IUnitContainer like so
var mapper = new MapperConfiguration(cfg =>
{
cfg.AddProfile<AutoMapperProfile>();
});
container.RegisterInstance<IMapper>(mapper.CreateMapper());
Now, in my controller, I want to pull a model from the database, then I want to map/cast it to my view-model.
I tried to do the following
var task = UnitOfWork.Tasks.Get(123)
.ProjectTo<TaskViewModel>();
But I can't seems to find the ProjectTo extension which I assumed it will be part of the AutoMapper project.
What is the correct way to project the viewModel if my AutoMapperProfile already created the mapping?
It seems to be in Automapper.QueryableExtensions:
https://github.com/AutoMapper/AutoMapper/blob/8dd104aa7390c12c97c4195cce6f6ff66de24f51/src/AutoMapper/QueryableExtensions/Extensions.cs
you can call it as long as the previous item in your linq change is IQueryable it seems.
Related
I am working with a ASP.NET Core WebAPI and I want to do CRUD for my objects called "Item".
I am using EF Core to work with a SQL Database and I have two models that represents my objects.
ItemDto - Data Transfer Object for an Item
ItemEntity - Database Object (represents a row in a table 1:1)
My HTTP GET one and HTTP GET many methods works in such way that it
Get ItemRepository instance
Fetch one or more ItemEntity
Map it to ItemDto by using AutoMapper
This is initalized in my constructor such as
m_itemDtoMapper = new Mapper(new MapperConfiguration(cfg => cfg.CreateMap<ItemEntity, ItemDto>()));
And in my WebAPI method I map it to a ItemDto with the following line (for the GET many case):
var itemDtos = m_itemDtoMapper.Map<IEnumerable<ItemEntity>, ICollection<ItemDto>>(items);
This works well and the AutoMapper is very powerful. The questions I have now is:
Is this the standard way of managing the relationship between database entities and data transfer objects?
In the CreateItem method, I need to do reverse mapping. Instead of mapping ItemEntity to ItemDto, I need to map a ItemDto to an ItemEntity. How shall I do this? Creating a copy of my mapper just with switched entities works but is that how its supposed to be done? i.e two mappers.
In your example, it seems that you initialize each time a new mapper instance. I would suggest you go along with dependency injection and make use of AutoMapper Mapping Profiles.
You can do it in three simple steps and I think it answers both of your questions:
Step 1:
Simply create a new class called MappingProfile or something similar:
public class MappingProfile: Profile
{
public MappingProfile()
{
CreateMap<User, AuthenticateDto>(); // One Way
CreateMap<User, UserDto>().ReverseMap(); // Reverse
}
}
Step 2: Register Automapper in Startup.cs
// Register AutoMapper
services.AddAutoMapper(Assembly.GetExecutingAssembly());
Step 3: Consume your mapper over DI
public UserService(IMapper mapper) {
_mapper = mapper;
}
// call it as you already did
_mapper.Map<User, UserDto>(user);
Hopefully it helps you :)
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 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.
I just started using the Entity Framework 1.0 recently and believe I am beginning to feel the pains everyone is talking about. I'm trying to use best practices so I have a set of DTO that get mapped to and from my Entities via AutoMapper.
The real catch is when I'm trying to update an object. The first gotcha was that I could not find a way to create a new entity, transfer the data from my DTO, and still have the entity ObjectContext realize that it has been changed. I used the following code:
public VideoDTO UpdateVideo(VideoDTO pVideo)
{
Video video = new Video();
Mapper.Map(pVideo, video);
context.Attach(video); //Successfully attaches
context.ApplyPropertyChanges("Videos", video); // no changes made as far as entity knows b/c it was attached in it's updated state
context.SaveChanges(); //doesn't save the entity
return pVideo;
}
I then figured, perhaps I need to just grab the entity from the database first, attach to the context, call the Map method on Mapper, then call SaveChanges. Here what I did:
public VideoDTO UpdateVideo(VideoDTO pVideo)
{
Video video = context.Videos.Where(v => v.VideoId == pVideo.VideoId).FirstOrDefault();
Mapper.Map(pVideo, video); //Error here: Can't change VideoId value on Video entity
//context.Attach(video);
//context.ApplyPropertyChanges("Videos", video);
context.SaveChanges();
return pVideo;
}
Now we get to the lovely EF issue of not being allowed to change the property, VideoId, because it's used by the EntityKey property on the Video entity. Lovely. I had setup the mappings so that when I mapped from my DTO to an EF Entity, the EntityKey property would get a value. Now I need a way to make an exception to that mapping rule, but have no clue where to begin. I suppose I could create a brand new Mapping rule right in this method and set the EntityKey & VideoId properties to be ignored, but that seems pretty sloppy. Furthermore, I'm not sure a mapping created at this point would stick. If it overrode the initial setup that allowed the DTO to map a value to the EntityKey on the entity, that would backfire in a whole different way.
Anyone have a better idea?
AutoMapper
Your first problem is that as far as I know AutoMapper is not designed to go from DTO->Entity only Entity->DTO. This could have changed recently so I'm not really sure. See this link for more information about what automapper is designed to do: The case for two way mapping
PK Mapping
You say: "Mapping rule right in this method and set the EntityKey & VideoId properties to be ignored, but that seems pretty sloppy"
I don't think thats sloppy at all. You really shouldn't touch a EntityKey/PK after its been persisted and probably should codify its staticness in some way.
Entity Framework
"Now we get to the lovely EF issue of not being allowed to change the property, VideoId, because it's used by the EntityKey property on the Video entity. Lovely."
Lovely? EF is not forcing you to not update your PK. Inside the generated models there is a property change check inside the setter for your keys. The solution would be to change the generated code. Depending on your model volatility this may not be practical but it is an option.
Try mapping to an existing object:
entity = Mapper.Map<MyDTO, NyEntity>(dto, entity);
And keep the Ignore()'s in place.
http://groups.google.com/group/automapper-users/browse_thread/thread/24a90f22323a27bc?fwc=1&pli=1
I'm in the same scenario.
The only solution I got is to Ignore the PK field in the mapping from DTO -> Entity.
Such Rule can be achieved by the following line of code during the Automapper Configuration:
Mapper.CreateMap<MyDTO, MyEntity>().ForMember("EntityPK",r=>r.Ignore());
As far as I know, the only way to get EF works with Detached Entities is mapping the DTO to the Entity you got from DB before the SaveChanges (as you did in the example).
This may help if you want to avoid putting .Ignore()s on every Entity you want to Map.
http://www.prosoftnearshore.com/blog/post/2012/03/14/Using-AutoMapper-to-update-Entity-Framework-properties.aspx
In essence, you'd configure AutoMapper to ignore all Entity properties that are not scalar:
AutoMapper.Mapper.CreateMap<EntityType, EntityType>()
.ForAllMembers(o => {
o.Condition(ctx =>
{
var members = ctx.Parent.SourceType.GetMember(ctx.MemberName); // get the MemberInfo that we are mapping
if (!members.Any())
return false;
return members.First().GetCustomAttributes(typeof(EdmScalarPropertyAttribute), false).Any(); // determine if the Member has the EdmScalar attribute set
});
});
Perhaps some additional work can be added to avoid resetting if the property is a PK (a property in the EdmScalarPropertyAttribute instance (EntityKey == true?) tells you this).
Please be aware that example provided by "Mauricio Morales" will work only if you do not use prefixes. If you use them then you need to change above code slightly in more or less way like this:
Mapper.CreateMap<tempOR_Order, OR_Order>()
.ForMember(m => m.OR_ID, exp => exp.Ignore())
.ForMember(m => m.OR_CU_ID, exp => exp.Ignore())
.ForAllMembers(o => o.Condition(ctx =>
{
var members = ctx.Parent.SourceType.GetMember(ctx.MemberName); // get the MemberInfo that we are mapping
if (!members.Any())
{
members = ctx.Parent.SourceType.GetMember("temp" + ctx.MemberName);
if (!members.Any())
return false;
}
return members.First().GetCustomAttributes(typeof(EdmScalarPropertyAttribute), false).Any(); // determine if the Member has the EdmScalar attribute set
}));
That is, you need to include additional checks inside if (!members.Any()) statement. Without this, function return false and mapping will not work.