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
3 Responses to “Domain entities vs presentation model objects, part 2: mapping” Leave your Comment
  1. Jonathan Birkholz says:

    Awesome posts. Funny how developers across the world can be working on the same things. :)

  2. Paul Hinett says:

    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

  3. Richard says:

    Paul,

    Good question. I’ll answer this with a blog post when I get back to town after the weekend.

Leave a reply

We love to hear your views.