AutoMapper feature: projection

I’m slowly filling in documentation for AutoMapper, which is turning out to be exactly as much fun as I estimated.

Projection

Projection transforms a source to a destination beyond flattening the object model.  Without extra configuration, AutoMapper requires a flattened destination to match the source type’s naming structure.  When you want to project source values into a destination that does not exactly match the source structure, you must specify custom member mapping definitions.  For example, we might want to turn this source structure:

public class CalendarEvent
{
    public DateTime EventDate { get; set; }
    public string Title { get; set; }
}

Into something that works better for an input form on a web page:

public class CalendarEventForm
{
    public DateTime EventDate { get; set; }
    public int EventHour { get; set; }
    public int EventMinute { get; set; }
    public string Title { get; set; }
}

Because the names of the destination properties do not exactly match up to the source property (CalendarEventForm.EventDate would need to be CalendarEventForm.EventDateDate), we need to specify custom member mappings in our type map configuration:

// Model
var calendarEvent = new CalendarEvent
    {
        EventDate = new DateTime(2008, 12, 15, 20, 30, 0),
        Title = "Company Holiday Party"
    };

// Configure AutoMapper
Mapper.CreateMap<CalendarEvent, CalendarEventForm>()
    .ForMember(dest => dest.EventDate, opt => opt.MapFrom(src => src.EventDate.Date))
    .ForMember(dest => dest.EventHour, opt => opt.MapFrom(src => src.EventDate.Hour))
    .ForMember(dest => dest.EventMinute, opt => opt.MapFrom(src => src.EventDate.Minute));

// Perform mapping
CalendarEventForm form = Mapper.Map<CalendarEvent, CalendarEventForm>(calendarEvent);

form.EventDate.ShouldEqual(new DateTime(2008, 12, 15));
form.EventHour.ShouldEqual(20);
form.EventMinute.ShouldEqual(30);
form.Title.ShouldEqual("Company Holiday Party");

The each custom member configuration uses an action delegate to configure each member.  In the above example, we used the MapFrom option to perform custom source/destination member mappings.  The MapFrom method takes a lambda expression as a parameter, which then evaluated later during mapping.  The MapFrom expression can be any Func<TSource, object> lambda expression.

Related Articles:

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Jimmy Bogard

I'm a technical architect with Headspring in Austin, TX. I focus on DDD, distributed systems, and any other acronym-centric design/architecture/methodology. I created AutoMapper and am a co-author of the ASP.NET MVC in Action books.
This entry was posted in AutoMapper. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://bloggingabout.net/blogs/ramon/ Ramon Smits

    I see benefits in using the automapper but why whould I use it for these kind of mappings? As doing the mapping in normal code would be much more readable.

    You could ofcourse just add this code into the mapper. Something like:

    Mapper.CreateMap(delegate(CalendarEvent src, CalendarEventForm dst){
    var date = src.EventDate;
    dst.EventData = date.Date;
    dst.EventHour = date.Hour;
    dst.EventMinute = date.Minute;
    });

    Much more readable the the mapping code.

  • Lubos

    I like Jimmy’s version because it’s done declarative way and without possible side-effects. That’s big win for me.

    Jimmy could still simplify .ForMember method

    for example:

    .ForMember(dest => dest.EventDate, opt => opt.MapFrom(src => src.EventDate.Date))

    could also become

    .ForMember(dest => dest.EventDate, src => src.EventDate.Date)

    I have no idea why MapFrom method is so important.

  • Mike

    Automapper is a great little tool, we just started using it on one of our projects. The syntax is very fluid. Good work!

  • Tim

    Is it possible to do partial mapping, ie we have large forms that are split across multiple pages. We have view models for each form but we have to map all of the data back.

    The requirement for all source members to be mapped makes this difficult.