M Raymaker
M Raymaker

Reputation: 1271

Automapper can't map nested collection

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')

Upvotes: 0

Views: 666

Answers (2)

Randy Gamage
Randy Gamage

Reputation: 1931

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));
        });

Upvotes: 1

M Raymaker
M Raymaker

Reputation: 1271

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();
        });

Upvotes: 0

Related Questions