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.
Last edited Feb 21 2009 at 10:54 PM by jbogard, version 1
Comments
jpark2 Sep 9 2009 at 9:21 PM 
The projection does not work on VB.Net projects. I am getting "Expression does not produce a value" error.

I found out that VB compiler cannot process the expression that does not return any value. VB translates c# function with return type void as a Sub (subroutine) and others as Function. And when the expression is usiing a Subroutine, the compiler errors out with "Expression does not produce a value" error message.

I tested modifying the IMemberConfigurationExpression.MapFrom() to return a boolean value rather than void, and always return true. After this change, I was able to compile it successfully, and the projection worked correctly.

I guess in order for this library to work correctly on VB.Net projects, all IMemberConfigurationExpression interface should be modified to return something rather than void.

I do prefer working in C#, but sometimes the client environment dictates language choices.

-jp

danthman Feb 3 at 1:37 AM 
Is there a slick way to handle the case where EventDate is null?

Or is your only recourse to use a ternary operator in your lambda? For example:
.ForMember(dest => dest.EventDate, opt => opt.MapFrom(src => src.EventDate == null ? -1 : src.EventDate.Date))

I've been using the ternary operator in my lambdas, but it gets to be a hassle if you are flattening a lot of sub-properties of a property that could be null. You end up mapping out almost the whole object by hand.

creaturita Thu at 3:34 PM 
Why is better this than overload the dto constructor? I have never undertood the differences.

blemoine Fri at 1:02 AM 
danthman:

I had a similar issue, I think for your example it may be easier (or less code) to simply use C#'s null coalescing operator.

Your example would go from this:
.ForMember(dest => dest.EventDate, opt => opt.MapFrom(src => src.EventDate == null ? -1 : src.EventDate.Date))

To this:
.ForMember(dest => dest.EventDate, opt => opt.MapFrom(src => src.EventDate ?? -1))

danthman Fri at 6:59 AM 
blemoine, I remembered the "??" operator when I was creating my map, but if you look closely, your replacement is not equivalent to the original. The original says, if src.EventDate is null, use -1, otherwise use src.EventDate.Date. Your replacement says, if src.EventDate is null, use -1, otherwise use src.EventDate. (Hint: the .Date part is missing).

Updating...
© 2006-2010 Microsoft | About CodePlex | Privacy Statement | Terms of Use | Code of Conduct | Advertise With Us | Version 2010.1.12.16187