0

I'm trying to figure out how to do this mapping in automapper 9.0.0. Can anybody help me out? I have these classes

class User
{
    public string Name { get; set; }
    public Address[] Addresses { get; set; }
}
class Address
{
    public string StreetName { get; set; }
}

class UserDto
{
    public string Name { get; set; }
    public PropertiesDto Properties { get; set; }
}

class PropertiesDto
{
    public AddressDto[] Addresses { get; set; }
}

class AddressDto
{
    public string StreetName { get; set; }
}

My goal is to place the array of addresses inside a 'PropertiesDto' object, where there eventually will be a lot of other arrays.

var user = new User { Name = "Foo", Addresses = new[] { new Address { StreetName = "Main St." } } };

        var config = new MapperConfiguration(cfg =>
        {
            cfg.CreateMap<Address[], AddressDto[]>();
            cfg.CreateMap<User, UserDto>()
                .ForMember(d => d.Properties.Addresses, opt => opt.MapFrom(s => s.Addresses));
        });

        IMapper mapper = new Mapper(config);
        var dtoUser = mapper.Map<UserDto>(user);
        Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(dtoUser));
        
        Console.WriteLine("Hit enter...");
        Console.ReadLine();

The code fails with this error message.

Unhandled exception. System.ArgumentException: Expression 'd => d.Properties.Addresses' must resolve to top-level member and not any child object's properties. You can use ForPath, a custom resolver on the child type or the AfterMap option instead. (Parameter 'lambdaExpression')

M Raymaker
  • 1,231
  • 5
  • 14
  • 31
  • have u tried to use search? https://stackoverflow.com/questions/11633021/automapper-expression-must-resolve-to-top-level-member – Leszek P Jul 16 '20 at 08:51
  • Yes, but obviously didn't use the right search term. I see now that automapper only can map for level 1 objects. – M Raymaker Jul 16 '20 at 09:10

2 Answers2

1

There is a special command for just this situation - if you're mapping to a property below the top level, then just use .ForPath (as the error message suggests) in place of .ForMember. Like this:

        var config = new MapperConfiguration(cfg =>
        {
            cfg.CreateMap<Address[], AddressDto[]>();
            cfg.CreateMap<User, UserDto>()
                .ForPath(d => d.Properties.Addresses, opt => opt.MapFrom(s => s.Addresses));
        });
Dharman
  • 30,962
  • 25
  • 85
  • 135
Randy Gamage
  • 1,801
  • 6
  • 22
  • 31
0

What worked for me was using reverse mapping.

var config = new MapperConfiguration(cfg =>
        {
            cfg.CreateMap<Address, AddressDto>();
            cfg.CreateMap<UserDto, User>()
                .ForMember(d => d.Addresses, opt => opt.MapFrom(s => s.Properties.Addresses))
                .ReverseMap();
        });
M Raymaker
  • 1,231
  • 5
  • 14
  • 31