I'm fairly new to AutoMapper. I have a hard time configuring AutoMapper to being able to map UserViewModel
with multiple TagViewModel
into a many-to-many relationship (RecipeEntity <-> TagEntity
) which is needed for Entity Framework Core where UserAndTagEntity
is the joining table.
Data Objects:
public class TagEntity
{
public string Name { get; set; }
public virtual ICollection<UserAndTagEntity> UserAndTags { get; set; } = new List<UserAndTagEntity>();
}
public class UserEntity
{
public string Name { get; set; }
public virtual ICollection<UserAndTagEntity> UserAndTags { get; set; } = new List<UserAndTagEntity>();
}
public class UserAndTagEntity
{
public int Id { get; set; }
public virtual UserEntity User { get; set; }
public virtual TagEntity Tag { get; set; }
}
public class UserViewModel
{
public string Name { get; set; }
public IList<TagViewModel> Tags { get; set; }
}
public class TagViewModel
{
public string Name { get; set; }
}
Test Example:
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<UserEntity, UserViewModel>()
.ForMember(
dto => dto.Tags,
opt => opt.MapFrom(x => x.UserAndTags.Select(y => y.Tag)));
cfg.CreateMap<UserViewModel, UserEntity>()
.ForMember(
dto => dto.UserAndTags,
opt => opt.MapFrom(x => x.Tags))
.AfterMap((model, entity) =>
{
foreach (var entityUserAndTag in entity.UserAndTags)
{
entityUserAndTag.User = entity;
}
});
cfg.CreateMap<TagViewModel, UserAndTagEntity>();
});
var user = new UserViewModel()
{
Name = "User",
Tags = new List<TagViewModel>
{
new TagViewModel {Name = "Tag 1"},
new TagViewModel {Name = "Tag 2"},
new TagViewModel {Name = "Tag 3"},
new TagViewModel {Name = "Tag 4"},
}
};
IMapper mapper = config.CreateMapper();
var map = mapper.Map<UserViewModel, UserEntity>(user);
This works partly - The issue I've is that Tag
on UserAndTagEntity
is null.
The mapping from UserViewModel
to UserEntity
can be achieved with the following configuration:
CreateMap<UserViewModel, UserEntity>()
// (1)
.ForMember(entity => entity.UserAndTags, opt => opt.MapFrom(model => model.Tags))
// (5)
.AfterMap((model, entity) =>
{
foreach (var entityUserAndTag in entity.UserAndTags)
{
entityUserAndTag.User = entity;
}
});
// (2)
CreateMap<TagViewModel, UserAndTagEntity>()
// (3)
.ForMember(entity => entity.Tag, opt => opt.MapFrom(model => model));
// (4)
CreateMap<TagViewModel, TagEntity>();
Explanation:
The line (1) is needed because the target and source property names do not match, so we just tell AutoMapper to map Tags
property of the UserViewModel
to UserAndTags
property of the UserEntity
.
Note that the mapping does not require that the source and target property types match. If they don't (as in this case), AutoMapper will map them using a separate configuration.
In our case, the source property type is IList<TagViewModel>
and target property type is ICollection<UserAndTagEntity>
. Ignore the collection types - AutoMapper knows how to convert them. What it doesn't know and needs to be specified is the mapping between the element types. In our case, from TagViewModel
to UserAndTagEntity
. Hence the need of mapping (2).
Inside the mapping (2), we have only one of the parts, so we use (3) to specify that - i.e. we map TagViewModel
to Tag
property of the UserAndTagEntity
. Again the types do not match, so we need the mapping (4) from TagViewModel
to TagEntity
.
With all that the final result will be UserEntity
instance with UserAndTags
collection populated with UserAndTagEntity
instances having correct Tag
property. Then the step (5) is used to populate the User
property of these instances.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With