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.
Source/destination types
// 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; }
}
Mapping configuration
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>())));
Version
Using AutoMapper v8.0.0.
Expected behavior
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.
Actual behavior
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
Note
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.
Related
I'm attempting to leverage AutoMapper in order to not have to manually write a lot of code mappings. This appears to be working fine for everything other than this one class:
CreateMap<AccountConnection, AccountConnectionDto>();
CreateMap<Account, AccountDto>();
CreateMap<Address, AddressDto>() // <--- this one
.ForMember(dest => dest.StreetAddress1, opt => opt.MapFrom(src => src.street_address_1))
.ForMember(dest => dest.StreetAddress2, opt => opt.MapFrom(src => src.street_address_2))
.ForMember(dest => dest.StreetAddress3, opt => opt.MapFrom(src => src.street_address_3));
If I don't manually map those 3 members, then when I run config.AssertConfigurationIsValid(); it throws.
Exception Details: 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
=================================================
Address -> AddressDto (Destination member list)
Proj.Data.Address -> Proj.API.AddressDto (Destination member list)
Unmapped properties:
StreetAddress1
StreetAddress2
StreetAddress3
I am using the following naming conventions in my profile:
SourceMemberNamingConvention = new LowerUnderscoreNamingConvention();
DestinationMemberNamingConvention = new PascalCaseNamingConvention();
These are the only properties with numbers in the property name so I dug in to the AutoMapper source on GitHub and found the Regex for the PascalCaseNamingConvention that I'm using in my project. It is:
(\p{Lu}+(?=$|\p{Lu}[\p{Ll}0-9])|\p{Lu}?[\p{Ll}0-9]+)
If I throw that in https://regex101.com and then test it against my property name ShippingAddress1 I get two matches, Shipping and Address1.
Ruh-roh! My source property name is shipping_address_1 (Don't ask) so that isn't going to work. Is this because my naming convention is broken, or should the PascalCaseNamingConvention match shipping_address_x to ShippingAddressX? (Went to raise an issue on the AutoMapper github but they ask newcomers to post on SO first, to see if people think it is a legitimate bug or not).
From my testing detailed below, I believe the naming conventions specified are the wrong way around:
public class Address
{
public string StreetAddress1 { get; set; }
}
public class AddressDto
{
public string street_address_1 { get; set; }
}
static void Main(string[] args)
{
// Prints nothing
PerformMappingTest(new PascalCaseNamingConvention(), new LowerUnderscoreNamingConvention());
// Prints "Test"
PerformMappingTest(new LowerUnderscoreNamingConvention(), new PascalCaseNamingConvention());
Console.ReadKey();
}
static void PerformMappingTest(INamingConvention source, INamingConvention destination)
{
var config = new MapperConfiguration(cfg => {
cfg.SourceMemberNamingConvention = source;
cfg.DestinationMemberNamingConvention = destination;
cfg.CreateMap<Address, AddressDto>();
});
var mapper = config.CreateMapper();
var address = new Address { StreetAddress1 = "Test" };
var addressDto = mapper.Map<Address, AddressDto>(address);
Console.WriteLine(addressDto.street_address_1);
}
I have two sources like
public class Source1
{
public int Id {get;set;}
public int Source2ID { get; set; }
... //Other Fields
}
Source2 like
public class Source2 : Entity
{
public int Id {get;set;}
public string Name { get; set; }
}
And a destination class as follows
public class destination
{
public int Id {get;set;}
public int Source2ID { get; set; }
public string Source2Name {get;set;}
... //Other Fields
}
What i'm trying to achieve is map the source2 name in the destination with the source2ID available in the source1.
What i have tried is to use a custom value resolver.
public class CustomResolver : IValueResolver<Source1, Destination, string>
{
private readonly IRepository<Source2> _suiteRepository;
public CustomResolver(IRepository<Source2> suiteRepository )
{
_suiteRepository = suiteRepository;
}
public string Resolve(Source1 source, Destination destination, string destMember, ResolutionContext context)
{
return _suiteRepository.Get(source.Source2ID).Name;
}
}
And then in the config, I'm creating the map as follows.
config.CreateMap<Source1, Destination>()
.ForMember(u => u.Name, options => options.ResolveUsing<CustomResolver>());
Following is the code that's calling the mapper
var source1 = await _repository
.GetAll()
.ToListAsync();
var destinationList = ObjectMapper.Map<List<Destination>>(source1);
This however produces the following error.
An unhandled exception occurred while processing the request.
MissingMethodException: No parameterless constructor defined for this object.
System.RuntimeTypeHandle.CreateInstance(RuntimeType type, bool publicOnly, bool wrapExceptions, ref bool canBeCached, ref RuntimeMethodHandleInternal ctor)
AutoMapperMappingException: Error mapping types.
Mapping types:
Destination
Source1
Type Map configuration:
Source1-> Destinatinon
Namespace.Source1 -> Namespace.Destination
Property:
Source2Name
lambda_method(Closure , List<Sourc1> , List<Destination> , ResolutionContext )
AutoMapperMappingException: Error mapping types.
I am new to AutoMapper, I have searched google but couldn't find something on this. I'm not sure if there is a better way to map these together.
Thanks
As Redirected by Lucian, CustomResolver needs to be initialized first. i.e From the Automapper documentation
Once we have our IValueResolver implementation, we’ll need to tell AutoMapper to use this custom value resolver when resolving a specific destination member. We have several options in telling AutoMapper a custom value resolver to use, including:
MapFrom< TValueResolver>
MapFrom(typeof(CustomValueResolver))
MapFrom(aValueResolverInstance)
Using the third option, solved the problem.
i.e
config.CreateMap<Source1, Destination>()
.ForMember(u => u.Name, options => options.ResolveUsing(new CustomResolver());
However in my case, since I had the repository as the parameter for the Resolver, I had to resolve the scoped Repository from the service provider .
Since, i'm using AbpBoilerplate, my code look like this.
using (var repo = Configuration.Modules.AbpConfiguration.IocManager.CreateScope().ResolveAsDisposable<IRepository<Source2>>())
{
config.CreateMap<Source1, Destination>()
.ForMember(u => u.Name, options => options.ResolveUsing(new CustomResolver(repo.Object)));
}
I am facing issue for mapping for nested objects using Auto Mapper method
Can any one give suggestion to map Source and Target class using auto mapper
For example this is sample code.
Source:
Main Class:
public class EMP {
public int empid {get;set;}
public int Name {get;set;}
public EMPDetails Employeedata {get;set;}//Here is inner object in main class
public EmpContact EmpContactdetails {get;set;}//Here is other inner object in main class
}
public Class EMPDetails {
public String Name {get;set;}
public EmpAdd EmpAddress {get;set;}//Here is inner Object in one class
}
public class EmpAdd {
public int id {get;set;}
public int Street {get;set;}
}
public class EmpContact {
public int id {get;set;}
public int Mobile {get;set;}
}
Target:
Main Class:
public class EMPInfo {
public int id {get;set;}
public int EmpName {get;set;}
public EMPPerdata Employeedata {get;set;}//Here is inner object in main class
public EmpContact EmpContactdetails {get;set;}//Here is other inner object in main class
}
public class EMPPerdata {
public String FullName {get;set;}
public EmpAdressInfo EmpAddress {get;set;}//Here is inner Object in one class
}
public class EmpAdressInfo {
public int id {get;set;}
public int Street {get;set;}
}
public class EmpContact {
public int id {get;set;}
public int Mobile {get;set;}
}
Original Answer
You'll need to create maps for both the outer and inner classes.
Ex:
Mapper.CreateMap<Emp,EmpInfo>();
Mapper.CreateMap<EmpDetails,EMPPerdata>();
etc...
Then:
EmpInfo Dest = Mapper.Map<Emp, EmpInfo>(srcObj);
That should work.
Edit (because that's not a great answer)
So, just wanted to add on this edit to show a few AutoMapper best practices because if you follow those best practices it's a lot easier to use.
AutoMapper Profiles
When using Automapper, unless you are mapping really really simple objects, the best way to set up your mapping is through using profiles. To use those profiles make certain to add a using AutoMapper; to your program and then you can create a mapping profile like so:
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<EMP, EMPInfo>();
}
}
Now, in any of the classes you want to use this profile you can use it like so:
Mapper.Initialize(cfg =>
{
cfg.AddProfile<MappingProfile>();
});
EMPInfo empInfo = Mapper.Map<EMP, EMPInfo>(emp);
Now... why would you ever go to all of the trouble to do this? You could have just as easily said Mapper.CreateMap<EMP, EMPInfo>(); Mapper.Map<EMP, EMPInfo(src); and it would be doing the same thing.
The benefits to doing it the way I stated above are three-fold.
Centralization of your mapping expressions. In my case, I usually have a single MappingProfiles class that holds all the Mapping profiles that I could use. This practice helps more to organize your code than anything else.
Reusability. Wherever in your program you need to use that mapping profile which will likely be more than just a single CreateMap() line you can just initialize that profile and then use it.
Unit Testing. Loading in profiles makes unit testing for AutoMapper really easy. And, unit testing will help you figure out exactly how to map nested objects.
Unit Testing AutoMapper
In your unit testing project (add one if you don't have it, it's a default project template in visual studio) you should have a class that holds the following unit test:
[TestMethod]
public void AutoMapperConfigIsValid()
{
Mapper.Initialize(cfg =>
{
cfg.AddProfile<MappingProfile>();
// add all profiles you'll use in your project here
});
Mapper.AssertConfigurationIsValid();
}
Now, if I were to run that unit test using the classes you laid out above and the mapping profile that I put above it will give me the error:
Unmapped Properties:
id
EmpName
This isn't the full error, just the most important part. What's happening is... AutoMapper cannot figure out where the id and EmpName fields on the EMPInfo Type should be mapped from.
To resolve this error we need to add mapping for particular members to our profile. Our profile becomes:
CreateMap<Source.EMP, Target.EMPInfo>()
.ForMember(dest => dest.id, opt => opt.MapFrom(src => src.empid))
.ForMember(dest => dest.EmpName, opt => opt.MapFrom(src => src.Name));
We add that in... run the test again... get a new error.
Add a custom mapping expression, ignore, add a custom resolver, or modify the destination type TestStuff.Target.EmpContact.
Context:
Mapping from type TestStuff.Source.EmpContact to TestStuff.Target.EmpContact
Exception of type 'AutoMapper.AutoMapperConfigurationException' was thrown.
This tells us that we need to add in a map for the nested members of the objects we are mapping.
To resolve this, we add the following map to the mapping profile:
CreateMap<Source.EmpContact, Target.EmpContact>();
Now what you need to do here, and this is sort of an arduous, but necessary, process, is keep running that configuration test and fixing the issues that it gives you until the test does not fail.
When all the issues are fixed, the mapping profile looks like this:
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<Source.EMP, Target.EMPInfo>()
.ForMember(dest => dest.id, opt => opt.MapFrom(src => src.empid))
.ForMember(dest => dest.EmpName, opt => opt.MapFrom(src => src.Name));
CreateMap<Source.EmpContact, Target.EmpContact>();
CreateMap<Source.EMPDetails, Target.EMPPerdata>()
.ForMember(dest => dest.FullName, opt => opt.MapFrom(src => src.Name));
CreateMap<Source.EmpAdd, Target.EmpAdressInfo>();
}
}
This mapping profile works great and will map your two objects fine.
Now, this map seem to be a lot of work for a little thing, but the benefits of doing it this way come with scale. If you are ever using AutoMapper in a large 'Adapter' type project, code structured this way is going to be easier to navigate, read and test.
Hopefully this helps a bit. Sorry if I went way too far at any point here but hopefully this answer can help anyone trying to use AutoMapper.
You can find documentation on some of the stuff I mentioned above in the AutoMapper documentation as well: https://github.com/AutoMapper/AutoMapper/wiki/Configuration
you will need to create maps for all your models
Mapper.CreateMap<EMP,EmpInfo>();
Mapper.CreateMap<EmpDetails,EMPPerdata>();
Mapper.CreateMap<EmpContact,EmpContact>();
Mapper.CreateMap<EmpAdd,EmpAdressInfo>();
then:
Mapper.Map<Emp, EmpInfo>(someEmp);
I have to classes Like
class A
{
public int id {get; set;}
}
class B
{
public C c {get; set;}
}
class C
{
public int id {get; set;}
public string Name {get; set;}
}
My requirement is to map id of class A to id of class C.
Now what I was doing till now was:
Mapper.CreateMap().ForMember(des => des.C.Id, src => src.MapFrom(x => x.id));
and it was working fine.
Now seems like Auto mapper has changed their implementation. and I am getting warning as below:
AutoMapper.Mapper.CreateMap()' is obsolete: 'Dynamically creating maps will be removed in version 5.0. Use a MapperConfiguration instance and store statically as needed, or Mapper.Initialize. Use CreateMapper to create a mapper instance.
I need to map some properties of classes which has different name and structure. Any help on this.
Previously
Mapper.CreateMap<Src, Dest>()
.ForMember(d => d.UserName, opt => opt.MapFrom(/* ????? */));
The problem here is mapping definitions are static, defined once and reused throughout the lifetime of the application. Before 3.3, you would need to re-define the mapping on every request, with the hard-coded value. And since the mapping configuration is created in a separate location than our mapping execution, we need some way to introduce a runtime parameter in our configuration, then supply it during execution.
This is accomplished in two parts: the mapping definition where we create a runtime parameter, then at execution time when we supply it. To create the mapping definition with a runtime parameter, we “fake” a closure that includes a named local variable:
Mapper.Initialize(cfg => {
string userName = null;
cfg.CreateMap<Source, Dest>()
.ForMember(d => d.UserName,
opt => opt.MapFrom(src => userName)
);
});
For more information see this
For one or more classes
cfg.CreateMissingTypeMaps = true;
cfg.CreateMap<Source, Dest>()
.ForMember(d => d.UserName,
opt => opt.MapFrom(src => userName)
);
cfg.CreateMap<AbcEditViewModel, Abc>();
cfg.CreateMap<Abc, AbcEditViewModel>();
});
In mapping class
IMapper mapper = config.CreateMapper();
var source = new AbcEditViewModel();
var dest = mapper.Map<AbcEditViewModel, Abct>(source);
Another way that seems a bit cleaner is to make a MappingProfile class which inherits from the Profile class of AutoMapper
public class MappingProfile:Profile
{
public MappingProfile()
{
CreateMap<Source1, Destination1>();
CreateMap<Source2, Destination2>();
...
}
}
Then you initialize the mapping with Mapper.Initialize(c => c.AddProfile<MappingProfile>()); in your startup code
That will allow you to use the mapping anywhere by calling
destination1Collection = source1Collection.Select(Mapper.Map<Source1, Destination1>);
Finally I found the resolution. I was doing: Mapper.Initialize{ Mapping field from source to destination }
in the App_start and adding this file to the global.asax--> Application_Start() --> GlobalConfiguration.
I need to add one more line inside my Mapper.Initialize which is cfg.CreateMissingTypeMaps = true;
Now this code will work for explicit mapping where two classes don't have the same structure and names of properties.
Apart from this, if we need to map properties of two class with the same structure the code Mapper.map(source, destination) will also work, which was not working earlier.
Let me know if someone is having difficulty with the solution. Thanks all for the above reply.
In my ASP.NET MVC 2 (RC) project - I'm using AutoMapper to map between a Linq to Sql class (Media) and a view model (MediaVM). The view model has a SelectList property for a drop down in the view. I have a custom value resolver to populate the SelectList property items from the db, but am wondering if there's a way to pass a couple values from the source model into the resolver (using ConstructedBy method?) to a) define the selected item and b) filter the items from the db. The source object gets passed into the custom resolver - but the resolver is used on several different view models with different types of source objects, so would rather define where to get the values from in my mapping config. Here is my view model:
public class MediaVM
{
public bool Active { get; set; }
public string Name { get; set; }
[UIHint("DropDownList")]
[DisplayName("Users")]
public SelectList slUsers { get; private set; }
}
The automapper mapping config:
Mapper.CreateMap<Media, MediaVM>()
.ForMember(dest => dest.slUsers, opt => opt.ResolveUsing<UsersSelectListResolver>());
It would be nice to be able to do something like this on the .ForMember mapping clause:
.ConstructedBy(src => new UsersSelectListResolver(src.UserID, src.FilterVal))
Is there a way to accomplish this?
I like that idea as a feature request. You can do something like that right now, with MapFrom:
ForMember(dest => dest.slUsers, opt => opt.MapFrom(src => new UsersSelectListResolver(src).Resolve(src));
I found your posting trying to do the same thing. I decided on a simple approach and skip trying to map to my select list directly via AutoMaper. I simply return an array into my ViewModel and reference that object for my select list. The array gets mapped, select list object does not. Simple, effective. And, IMHO each is doing it's intended task - the mapper maps, the ViewModel does the layout
View Model code:
[DisplayName("Criterion Type")]
public virtual CriterionType[] CriterionTypes { get; set; }
[DisplayName("Criterion Type")]
public SelectList CriterionTypeList
{
get
{
return new SelectList(CriterionTypes, "Id", "Key");
}
}
my mapper:
Mapper.CreateMap<Criterion, CriterionForm>()
.ForMember(dest => dest.CriterionTypeList, opt => opt.Ignore());