This is the second half of a two-part article. Read the first half here: Domain entities vs presentation model objects.
In my last post, I wrote about the difference between domain entities and presentation model objects. Remember my two task classes — the transactional domain entity and the UI presentation object? They’re very similar, and this could lead to a lot of ugly hand-written plumbing code mapping fields on one to the other.
// Task domain entity.
public class Task
{
public int Id;
public string Name;
public DateTime? DueDate;
// ...etc.
}
// Task presentation model object.
public class TaskView
{
public int Id;
public string Name;
public string DueDate;
public bool IsUnscheduled;
public bool IsOverDue;
public long SortIndex;
}
Instead, I’m using an open-source .NET library called AutoMapper by Jimmy Bogard — an object-object mapper (OOM) that sets values from one type to another.
Setting up a map from one type to another is dead simple — AutoMapper will automatically match fields with the same name. For other stuff we use lambda expressions, or delegate to another class. Here’s what my Task-to-TaskView mapping looks like:
// Set up a map between Task and TaskView. Note fields with the same names are
// mapped automagically!
Mapper.CreateMap<Task, TaskView>()
.ForMember(dest => dest.DueDate, opt => opt.AddFormatter<DueDateFormatter>())
.ForMember(dest => dest.SortIndex, opt => opt.ResolveUsing<SortIndexResolver>());
That was easy! Note I’m using a custom value formatter for the DueDate:
public class DueDateFormatter : IValueFormatter
{
public string FormatValue(ResolutionContext context)
{
DateTime? d = context.SourceValue as DateTime?;
if (d.HasValue)
return d.Value.ToString("dddd MMM d");
else
return "Anytime";
}
}
…and a custom resolver for the sort index (an integer derived from the task’s due date). Note that, while a formatter transforms one field by itself; a resolver examines the whole object to derive a value:
public class SortIndexResolver : IValueResolver
{
public ResolutionResult Resolve(ResolutionResult source)
{
Task t = source.Value as Task;
DateTime sortDate = t.DueDate.HasValue ?
t.DueDate.Value : DateTime.MaxValue;
long sortIndex =
Convert.ToInt64(new TimeSpan(sortDate.Ticks).TotalSeconds);
return new ResolutionResult(sortIndex);
}
}
With dedicated classes for formatting and resolving values, tests become very easy to write (although I did write my own ShouldFormatValueAs() test helper extension method):
[TestFixture]
public class When_displaying_a_due_date
{
[Test]
public void Should_display_null_values_as_anytime()
{
new DueDateFormatter().ShouldFormatValueAs<DateTime?>(null, "Anytime");
}
[Test]
public void Should_format_date()
{
new DueDateFormatter().ShouldFormatValueAs<DateTime?>(
new DateTime(2009, 02, 28), "Saturday Feb 28");
}
}
Putting it to practice, it becomes a one-liner to create a new TaskView instance given a Task.
// Grab a Task from the repository, and map it to a new TaskView instance. Task task = this.tasks.GetById(...); TaskView taskView = Mapper.Map<Task, TaskView>(task);
Awesome!
March 2nd, 2009 | 3 Comments



March 3rd, 2009 at 2:51 pm
Awesome posts. Funny how developers across the world can be working on the same things. :)
August 15th, 2009 at 12:32 am
Can you provide an example of how to unit test a controller which uses a ViewModel class that get’s automapped?
I am struggling to work out how i can Mock an automapped ViewModel to test my controllers actions.
Thanks,
Paul
August 15th, 2009 at 10:25 am
Paul,
Good question. I’ll answer this with a blog post when I get back to town after the weekend.