9

I've read the nested mapping wiki page but it appears to not like multiple levels of nesting. I've got the following maps created and classes defined.

AutoMapper.Mapper.CreateMap<Address, AddressDTO>();
AutoMapper.Mapper.CreateMap<MatchCompanyRequest, MatchCompanyRequestDTO>();

public class MatchCompanyRequest
{
    Address Address {get;set;}
}

public class MatchCompanyRequestDTO
{
    public CompanyInformationDTO {get;set;}
}

public class CompanyInformationDTO {get;set;}
{
    public string CompanyName {get;set;}
    public AddressDTO Address {get;set;}
}

But the following code...

// works
matchCompanyRequestDTO.companyInformationDTO.Address =
    AutoMapper.Mapper.Map<Address, AddressDTO>(matchCompanyRequest.Address);

// fails
matchCompanyRequestDTO =
    AutoMapper.Mapper
        .Map<MatchCompanyRequest, MatchCompanyRequestDTO>(matchCompanyRequest);

Does this deep nesting work and I have it configured improperly? Or is this kind of nesting not yet supported?

-- Edit

For anyone interested, I am not in control of the DTOs.

ryan
  • 6,541
  • 5
  • 43
  • 68

3 Answers3

6

It lacks the mapping from Address to CompanyInformationDTO, as those objects are on the same nest-level.

The map is created for MatchCompanyRequest -> MatchCompanyRequestDTO, but it is unable to figure out whether it can map Address to CompanyInformationDTO.

So your MatchCompanyRequestDTO could in fact have same declaration as your CompanyInformationDTO:

public class MatchCompanyRequestDTO
{
    public string CompanyName {get;set;}
    public AddressDTO Address {get;set;}
}

This of course only affects you if you want to use automatic mapping. You still can configure your maps manually, but it seems like the DTOs should be fixed instead, let's try anyway:

public class CustomResolver : ValueResolver<Address, CompanyInformationDTO>
{
    protected override CompanyInformationDTO ResolveCore(Address source)
    {
        return new CompanyInformationDTO() { Address = Mapper.Map<Address, AddressDTO>(source) };
    }
}
// ...

AutoMapper.Mapper.CreateMap<MatchCompanyRequest, MatchCompanyRequestDTO>()
    .ForMember(dest => dest.companyInformationDTO, opt => opt.ResolveUsing<CustomResolver>().FromMember(src => src.Address)); // here we are telling to use our custom resolver that converts Address into CompanyInformationDTO
Bartosz
  • 3,318
  • 21
  • 31
  • How would something like this be configured if you aren't using the static `Mapper` instance? – dougajmcdonald Sep 20 '17 at 06:49
  • Then you just use your instance instead of `AutoMapper.Mapper`? I don't really know - been years since I've used AutoMapper... – Bartosz Sep 20 '17 at 07:26
  • Yeah the best practice these days is to create a profile and feed it into the configuration so you can DI it and not to use the static instances. I'm curious as I see this as a common solution and wonder how it fits in with current best practice. – dougajmcdonald Sep 20 '17 at 08:17
  • But why would that be different? you call `CreateMap` on your injected instance and rest stays the same... – Bartosz Sep 20 '17 at 09:00
  • you don't `CreateMap` on your injected instance, you create a profile and call `CreatMap` within that as it inherits from `Profile`. In the resolver you don't have access to the injected instance. – dougajmcdonald Sep 20 '17 at 09:33
5

The important thing is you define how deeper is your navigation, to previne the stackoverflow problems. Imagine this possibility:

You have 2 entities Users and Notifications in NxN model (And you have DTOs object to represent that), when you user auto mapper without set MaxDepth in you mapper expression, "Houston we have a problem" :).

The code below show a workaround to resolve this for all Mappers. If you want can be defined to each mapper. Like this Question

Solution 1 (Global Definition)

public class AutoMapperConfig
{
    public static void RegisterMappings()
    {
        Mapper.Initialize(mapperConfiguration =>
        {
            mapperConfiguration.AddProfile<DomainModelToYourDTOsMappingProfile>();
            mapperConfiguration.AddProfile<YourDTOsToDomainModelMappingProfile>();
            mapperConfiguration.AllowNullCollections = true;
            mapperConfiguration.ForAllMaps(
                (mapType, mapperExpression) => {
                    mapperExpression.MaxDepth(1);
                });
        }
    }

Solution 2 (For each Mapper)

 public class AutoMapperConfig
 {
     public static void RegisterMappings()
     {
         Mapper.CreateMap<User, DTOsModel>()
               .MaxDepth(1);
     }
 }
Solarcloud
  • 555
  • 1
  • 7
  • 15
Rodrigo Couto
  • 61
  • 1
  • 3
0

Consider the following instead:

public class MatchCompanyRequest
{
    Address Address {get;set;}
}

public class MatchCompanyRequestDTO
{
    public string Name {get;set;}
    public AddressDTO Address {get;set;}
}

public class AddressDTO
{
    ....
}

Your DTO objects need to have the same structure as your domain objects for the default mapping conventions to work in AutoMapper.

Look at this: https://github.com/AutoMapper/AutoMapper/wiki/Projection It will explain the Projection for you, you could customize it to work the way you have it.

Sam
  • 15,336
  • 25
  • 85
  • 148