I need to map a model object coming from API to my actual entity object on DbContext
. It is used when creating a new machine object using a POST action.
As always, I created a simple map for the source/destination objects. In this case we consider the source object as the API model and the destination object as the entity. Also the model has just a subset of properties of the entity.
// Destination (entity on DbContext)
public class Machine
{
public long Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string MnmConfiguration { get; set; }
public MachineBootStatus Status { get; set; }
public long MachineDriverId { get; set; }
public MachineDriver MachineDriver { get; set; }
public string DriverConfiguration { get; set; }
public string DriverStatus { get; set; }
}
// Source (from API controller)
public class MachineCreateModel
{
public string Name { get; set; }
public string Description { get; set; }
public string MnmConfiguration { get; set; }
public long MachineDriverId { get; set; }
}
public class DomainProfile : Profile
{
public DomainProfile()
{
//CreateMap<MachineCreateModel, Machine>();
// Update 2019/01/30 with proposed solution
CreateMap<MachineCreateModel, Machine>(MemberList.Source);
}
}
I'm using Unity DI container and the configuration of AutoMapper is this:
container = new UnityContainer();
// ... some other configurations...
container.RegisterType<IMapper, Mapper>(new InjectionConstructor(new MapperConfiguration(cfg => cfg.AddProfile<DomainProfile>())));
Using AutoMapper v8.0.0.
I expect to obtain a simple automatic mapping without errors, since my source model is just a subset of properties of the destination model, with same names.
I get this error about unmapped properties when I hit this line of code:
Machine entity = Mapper.Map<Machine>(request.Machine);
[14:08:34.363 8 2e62361a INF] Creating new machine: TEST M1
[14:08:36.205 8 bd577466 ERR] An unhandled exception has occurred while executing the request.
AutoMapper.AutoMapperConfigurationException:
Unmapped members were found. Review the types and members below.
Add a custom mapping expression, ignore, add a custom resolver, or modify the source/destination type
For no matching constructor, add a no-arg ctor, add optional arguments, or map all of the constructor parameters
=================================================================================================
AutoMapper created this type map for you, but your types cannot be mapped using the current configuration.
MachineCreateModel -> Machine (Destination member list)
MyApplication.Dcs.Application.Models.MachineCreateModel -> MyApplication.Dcs.Domain.Entities.Machine (Destination member list)
Unmapped properties:
Id
Status
MachineDriver
DriverConfiguration
DriverStatus
at AutoMapper.ConfigurationValidator.AssertConfigurationIsValid(IEnumerable`1 typeMaps)
at lambda_method(Closure , MachineCreateModel , Machine , ResolutionContext )
at lambda_method(Closure , Object , Object , ResolutionContext )
at AutoMapper.Mapper.AutoMapper.IMapper.Map[TDestination](Object source)
at MyApplication.Dcs.Application.Commands.MachineCreateCommandHandler.Handle(MachineCreateCommand request, CancellationToken cancellationToken) ..Commands\MachineCreateCommand.cs:line 28
In my solution I've many projects and 3 of them are making use of AutoMapper (same version for all). There're 3 different DomainProfile.cs
files (1 for each project) with the needed mappings.
In the other 2 DomainProfile
classes I've some manual mappings (see example below) because I need to "translate" an object with italian property names to another one with english property names. So there're many lines for each object mapping, such as:
CreateMap<ArticleCreateUpdateModel, Articoli>()
.ForMember(d => d.Categoria, opt => opt.MapFrom(src => src.Category))
.ForMember(d => d.CodiceArticolo, opt => opt.MapFrom(src => src.Code))
.ForMember(d => d.Descrizione, opt => opt.MapFrom(src => src.Description))
.ForMember(d => d.Famiglia, opt => opt.MapFrom(src => src.Family))
.ForMember(d => d.Note, opt => opt.MapFrom(src => src.Note))
...
I don't know if the usage of those manual members mapping on one or more DomainProfile
class, obliges me in some way to always explain all the subsequent mappings, even if they should be simple like those of this example.
By default, AutoMapper validates the destination properties. As there are neither matching properties nor ForMember
constructs for a bunch of properties in your destination type you get the exception above.
Try to validate on the source properties instead:
CreateMap<ArticleCreateUpdateModel, Articoli>(MemberList.Source)
.ForMember(d => d.Categoria, opt => opt.MapFrom(src => src.Category))
// ...
Remark: On the other hand, I have to mention that this is the typical case when AutoMapper is an overkill. Apart from trivial cases I would never use it anymore.
I had to use it in a project for more than a year but actually it is only good for making simple things more complicated than necessary. Some FromDto
and ToDto
[extension] methods are just simpler, faster, easier to debug and more reactive to code changes. Mapping between different class layouts or property names often results practically as much code (or even more with tons of lambdas) as simply writing the mapping manually. See also this link.