AutoMapper is really handy tool used to map properties from one object to another. Very useful in situations such as mapping your domain objects into simplified POCO classes you would expose from an API. For more visit their site. Having played with it a bit I have stumbled across a couple of cool little feature you may not know about.
Flattening
This can be useful if the structure of your two objects do not match, say for example you have the classes:
public class Address { public string Street { get; set; } public string City { get; set; } public string PostCode { get; set; } } public class Phone { public string Home { get; set; } public string Mobile { get; set; } } public class Contact { public string Name { get; set; } public Address Address { get; set; } public Phone Phone { get; set; } } public class FlattenedContact { public string Name { get; set; } public string AddressStreet { get; set; } public string AddressCity { get; set; } public string AddressPostCode { get; set; } public string Home { get; set; } public string Mobile { get; set; } }
**You’ll notice that the property naming convention on the ‘FlattenedContact’ is different, this is on purpose, to show you how AutoMapper handles things differently.**
The simplest way to map, is just use
Mapper.CreateMap<FlattenedContact, Contact>();
And then call
var contact = Mapper.Map<FlattenedContact>(myContact);
Except it’s not quite that simple. AutoMapper, in this instance, will not match the properties coming from the ‘Phone’ class, this is because the name of the properties they correspond to on the flattened object (‘Home’ & ‘Mobile’) are not prefixed with ‘Phone’ (the name of the source class). Whereas the ‘Address’ properties will be matched, because they are called things such as ‘AddressCity’ etc. Makes sense? If you do not wish to re-name your properties, there is something you can do to tell AutoMapper:
Mapper.CreateMap<Contact, FlattenedContact>() .ForMember(dest => dest.Mobile, opt=> opt.MapFrom(src=> src.Phone.Mobile)) .ForMember(dest => dest.Home, opt=> opt.MapFrom(src=> src.Phone.Home));
Using this mapper instead of the simple previous one gives AutoMapper the information it needs, so all will be fine and dandy!
Un-flattening
As the heading may suggest, this is the opposite of what we wanted to achieve before. We will stick with the same example classes we just used – imagine this time we want to convert from ‘FlattenedContact’ to ‘Contact’. To do this, you need to set a simple mapper for the custom type within the complex object and set rules to ensure AutoMapper adds them when mapping that object. Put simply, you create a map from ‘FlattenedContact’ to both ‘Address’ and ‘Phone’. It will look a little like this:
Mapper.CreateMap<FlattenedContact, Address>(); Mapper.CreateMap<FlattenedContact, Phone>(); Mapper.CreateMap<FlattenedContact, Contact>() .ForMember(dest=> dest.Phone, opt=> opt.MapFrom(src=>src)) .ForMember(dest=> dest.Address, opt=> opt.MapFrom(src=> src));
Then simply call:
var contact = Mapper.Map<Contact>(myFlattenedContact);
As with flattening, this only works with one convention. In this case only the Phone class is mapped correctly, the address properties are not. This is because it does not recognise the name of property “AddressCity” as a match to just “City”. You will have to explicitly do some member mapping for this, one option would be to map them all individually:
.ForMember(dest => dest.Address.City, opt => opt.MapFrom(src => src.AddressCity)) .ForMember(dest => dest.Address.PostCode, opt => opt.MapFrom(src => src.AddressPostCode)) .ForMember(dest => dest.Address.Street, opt => opt.MapFrom(src => src.AddressStreet));
Alternatively, you could tell automapper to look out anything with a specified prefix in the property name, like so:
Mapper.RecognizePrefixes("Address"); Mapper.CreateMap<FlattenedContact, Address>(); Mapper.CreateMap<FlattenedContact, Phone>(); Mapper.CreateMap<FlattenedContact, Contact>() .ForMember(dest => dest.Phone, opt => opt.MapFrom(src => src)) .ForMember(dest => dest.Address, opt => opt.MapFrom(src => src));
Reverse Map
This is a nice and simple one. If you’re mapping from one object to another, you can call
Mapper.CreateMap<Source, Destination>().ReverseMap();
Instead of
Mapper.CreateMap<Source, Destination>(); Mapper.CreateMap<Destination, Source>();
Creating Your Own Rules
Sometimes you may want to do something complicated during your map, such as casting a property. This can be done by setting up a rule. To make this happen, you need to create a class that inherits from AutoMapper’s ‘TypeConverter’ class
public class CommaDelimitedStringConverter : TypeConverter<string, IEnumerable<string>>; { //This method is a v.simple one to convert from a comma //delimted string and output it as a list of strings protected override IEnumerable<string> ConvertCore(string source) { return source.Split(',').ToList(); } }
and then register this rule using
Mapper.CreateMap<string, IEnumerable<string>>() .ConvertUsing<CommaDelimitedStringConverter>();
Alternatively you can just pass in a Func, for example:
Mapper.CreateMap<string, int>().ConvertUsing(Convert.ToInt32);
EDIT: As of version 4.2 of AutoMapper, registrations are not longer static and look a little more like:
Mapper.Initialize(cfg => { cfg.CreateMap<FlattenedContact, Contact>(); cfg.CreateMap<MyType, MyOtherType>(); });
Happy Mapping!