Search code examples
asp.net-mvcautomapperasp.net-mvc-5.2

Automapper - Mapping entity types to nested view models


Lets say I have an EF entity like this:

public class Person 
{
    public string Name { get; set; }
    public string Psuedonym { get; set; }
    public bool HasPsuedonym { get; set; }
}

From this entity I want to map to a viewmodel which looks like this:

public class PersonViewModel 
{
    public string Name { get; set; }
    public PsuedonymViewModel { get; set; }
}

Which contains this other view model:

public class PsuedonymViewModel 
{
    public string Psuedonym { get; set; }
    public bool HasPsuedonym { get; set; }
}

What mappings do I need to setup?

I have a generic mapping setup which creates the following mappings:

Person > PersonViewModel - memberlist.dest
PersonViewModel > Person - memberlist.source

PsuedonymViewModel > Person - memberlist.dest
Person > PsuedonymViewModel - memberlist.source

The generic mappings only map the properties which are present in the source or destination, depending on which way the mapping is so that I don't have to add a whole bunch of ignores in manually for properties which don't match.

When I have this setup, automapper says:

AutoMapper.AutoMapperConfigurationException: The following property on MyApplication.PsuedonymViewModel cannot be mapped: 
    Psuedonym 
Add a custom mapping expression, ignore, add a custom resolver, or modify the destination type MyApplication.PsuedonymViewModel.
Context:
    Mapping to property Psuedonym from System.String to MyApplication.PsuedonymViewModel

I understand this message as when trying to map to PsuedonymViewModel from the Person entity there is no mapping from string to Psuedonym. I can solve this with a manual mapping like this:

CreateMap<string, PsuedonymViewModel>()
    .ForMember(x => x.HasPsuedonym, opt => opt.Ignore())
    .ForMember(x => x.Psuedonym, opt => opt.MapFrom(y => y));

Whilst this maps the Psuedonym property I have to ignore the HasPsuedonym property which is not what I'm after, but because it's mapping from string there isn't anything else I can work with.

It feels like I've done something basic wrong here but I can't figure it out, any thoughts greatly appreciated.


Solution

  • As you are using Person to build PsuedonymViewModel as well as PersonViewModel you have to build both mappings, and in root object mapping just point to build the property from the same source object:

    cfg.CreateMap<Person, PsuedonymViewModel>();
    cfg.CreateMap<Person, PersonViewModel>()
        .ForMember(x=>x.Psuedonym, m=>m.MapFrom(p=>p));