A Few Extra AutoMapper Features

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!

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s