Source models:
public class ExampleModel
{
public int Id { get; set; }
public float Something { get; set; }
public string Info { get; set; } = "";
}
public class ExampleModelContainer
{
public int Id { get; set; }
public ExampleModel Model { get; set; } = null!;
}
public class LargeEntity
{
public int Id { get; set; }
public List<ExampleModelContainer> ModelEntries { get; set; } = null!;
}
Target models:
public class ExampleModelDto
{
public float Something { get; set; }
public string Info { get; set; } = "";
}
public class LargeEntityDto
{
public int Id { get; set; }
public List<ExampleModelDto> Models { get; set; } = null!;
}
AutoMapper profile:
public class AutoMapperProfile : Profile
{
public AutoMapperProfile()
{
CreateMap<ExampleModel, ExampleModelDto>();
// One of my attempt, I thought converting container into model, and automatically example model into DTO. Failed
CreateMap<ExampleModelContainer, ExampleModel>()
.ConvertUsing(x => x.Model);
CreateMap<LargeEntity, LargeEntityDto>()
.ForMember(x => x.Models,
opt => opt.MapFrom(y => y.ModelEntries));
}
}
Expected result:
LargeEntities' List<ExampleModelContainer>
collection is mapped into List<ExampleModelDto>
.
Actual result:
Unhandled exception. AutoMapper.AutoMapperConfigurationException: The following member on AutoMapperExps.Models.LargeEntityDto cannot be mapped:
Models
Add a custom mapping expression, ignore, add a custom resolver, or modify the destination type AutoMapperExps.Models.LargeEntityDto.
Context:
Mapping to member Models from AutoMapperExps.Models.LargeEntity to AutoMapperExps.Models.LargeEntityDto
Exception of type 'AutoMapper.AutoMapperConfigurationException' was thrown.
at AutoMapper.Configuration.ConfigurationValidator.AssertConfigurationIsValid(IEnumerable`1 typeMaps)
at AutoMapper.Configuration.ConfigurationValidator.AssertConfigurationExpressionIsValid(IEnumerable`1 typeMaps)
at AutoMapper.MapperConfiguration.AssertConfigurationIsValid()
at Program.Main(String[] args) in D:\Experiments\AutoMapperExps\Program.cs:line 1
How I see it possible to be made:
- Mapper takes source
List<ExampleModelContainer>
collection - Mapper Selects
Model
property from each element of collection - Mapper projects ExampleModel (returned from property above) into ExampleModelDto
- Mapper returns
List<ExampleModelDto>
as a final result.
Note: This is made for Entity Framework queries. I want to do most projection operations on server-side.
CodePudding user response:
TLDR: AutoMapper throws the exception because it cannot find a mapping ExampleModelContainer -> ExampleDto
. If you define a mapping the error is fixed, e.g.:
CreateMap<ExampleModelContainer, ExampleModelDto>()
.IncludeMembers(x => x.Model);
The mapping tries to take several steps at once. If I understand your sample right, the configured mappings are like this:
ExampleModel -> ExampleDto
ExampleModelContainer -> ExampleModel
LargeEntity -> LargeEntityDto
When mapping the Models
property in for LargeEntityDto, you are trying to map a List<ExampleModelContainer>
without further configuration to a List<ExampleModelDto>
. It may seem clear for AutoMapper to take the route
ExampleModelContainer -> ExampleModel -> ExampleDto
in this case, because there is only one path. But what if there were several paths to get from a ExampleModelContainer
to a ExampleDto
? The library would not be able to make a decision that solves all possible combinations.
This means that you as a developer have to make the decision by configuring a mapping from ExampleModelContainer
to ExampleModelDto
, e.g.:
CreateMap<ExampleModelContainer, ExampleModelDto>()
.IncludeMembers(x => x.Model);
This way, AutoMapper has a direct mapping between the types and is able to map the properties. See this fiddle to test.