AutoMapper in NerdDinner

Speaking of NerdDinner, Scott asked me to use it to create an AutoMapper example. logo

AutoMapper, the brainchild of Jimmy Bogard, is an object-to-object mapper.  What that means is up to you – but we’ll use it here to map from the domain model to a view model.  The view model is an object heirarchy that represents the screen.  It’s as dumb as possible, just like the view.

We get a lot of nice things out of it and it helps us go faster.  You can read more about it AutoMapper from Jimmy or at the website on Codeplex.

For starters, NerdDinner isn’t the best scenario in which to apply AutoMapper.  NerdDinner is very small so there’s not much reuse to harvest. For example, if you format dates the same way a million times you can use AutoMapper to only write that formatting code once.  In a small application you may format dates two ways and only use the resulting text in two views.  It doesn’t make a lot of sense to extract a class just for that – it will seem like a lot of overhead. 

Also NerdDinner doesn’t have a rich domain model – there’s just not that much to do.  So the AutoMapper feature of flattening complex hierarchies can’t be appreciated.

I posted the result of this quick and dirty spike as a sample project – hopefully this will help you get started looking at it.

First, I copypasted a class to bootstrap AutoMapper:

namespace NerdDinner.Helpers.AutoMapper
{
    public class AutoMapperConfiguration
    {
        public static void Configure()
        {
            Mapper.Initialize(x => x.AddProfile<ViewModelProfile>());
        }
    }
}

.. which will be called when the application starts:

void Application_Start()
{
    AutoMapperConfiguration.Configure();

    RegisterRoutes(RouteTable.Routes);

    ViewEngines.Engines.Clear();
    ViewEngines.Engines.Add(new MobileCapableWebFormViewEngine());
}

I copypasted another class that will check the mapping configuration for errors, providing fast feedback should I make a mistake.

[TestClass]
public class AutoMapperConfigurationTester
{
    [TestMethod]
    public void Should_map_dtos()
    {
        AutoMapperConfiguration.Configure();
        Mapper.AssertConfigurationIsValid();
    }
}

Now I’m ready to begin creating the view model and configuring AutoMapper.

To design a view model, start with the screen.  What’s displayed will be represented in the model.  Again: the view model is an object hierarchy that represents the user interface.  I picked the Dinner Details screen, by the way.

The current model being used by the view was the Dinner entity itself.  There was a lot of formatting in the view and a lot of duplication.

Almost every property was surrounded by code that would HtmlEncode it (it will be nice to have AutoMapper do this for us):

<%= Html.Encode(Model.Title) %>

And there is a lot of formatting to do:

<abbr class="dtstart" title="<%= Model.EventDate.ToString("s") %>">
    <%= Model.EventDate.ToString("MMM dd, yyyy") %>
    <strong>@</strong>
    <%= Model.EventDate.ToShortTimeString() %>
</abbr>

Imagine a project with 300 screens and a team of analysts and you can imagine that specifying this formatting over and over again in requirements documents and planning would become tedious.  Not to mention coding it.  It’d be easier to just say: “Format this date in the standard way.” You can also imagine the security implications of forgetting to encode even one value.

In converting these screens to use a view model instead of the domain model I didn’t want to change existing functionality.  So I took this:

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<NerdDinner.Models.Dinner>"

and changed it to this:

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<DinnerDetailsViewModel>"

See what I did there?  I just changed the type of the Model property to a new DinnerDetailsViewModel type.

The view will receive a view model mapped from the domain model when I apply a special action filter to the controller action:

[AutoMap(typeof(Dinner), typeof(DinnerDetailsViewModel))]

The code’s in the sample, straight from Jimmy’s post.

I started out with DinnerDetailsViewModel being an empty class definition and used Resharper to generate each property as I encountered it.  I removed the formatting and the ubiquitous encoding from the parameters, turning this:

<div id="dinnerDiv" class="vevent">

    <h2 class="summary"><%= Html.Encode(Model.Title) %></h2>

    <p>
        <a href="http://feeds.technorati.com/events/<%= Url.AbsoluteAction("Details", new { id = Model.DinnerID }) %>">
            Add event to your calendar (iCal)
        </a>
    </p>

    <p>
        <strong>When:</strong>
<abbr class="dtstart" title="<%= Model.EventDate.ToString("s") %>">
<%= Model.EventDate.ToString("MMM dd, yyyy") %>
<strong>@</strong>
<%= Model.EventDate.ToShortTimeString() %>
</abbr>

into this:

<h2 class="summary"><%= Model.Title %></h2>

<p>
    <a href="http://feeds.technorati.com/events/<%= Url.AbsoluteAction("Details", new { id = Model.DinnerID }) %>">
        Add event to your calendar (iCal)
    </a>
</p>

<p>
    <strong>When:</strong>
    <%= Model.EventDate %>
</p>

I had to write a formatter to replace the custom formatting performed by the view.

public class AttendeeNameFormatter : BaseFormatter<string>
{
    protected override string FormatValueCore(string value)
    {
        return value.Replace("@", " at ");
    }
}

See how testable that is; small and reusable?

I also moved some methods that were previously being called from the view directly into the domain model which AutoMapper will evaluate at runtime.

Before:

<p id="whoscoming">
    <strong>Who's Coming:</strong>
    <%if (Model.RSVPs.Count == 0){%>
          No one has registered.
    <% } %>
</p>

After:

<p id="whoscoming">
    <strong>Who's Coming:</strong>
    <%if (Model.IsNobodyRegistered){%>
          No one has registered.
    <% } %>
</p>

The view model for this screen ends up looking like this:

public class DinnerDetailsViewModel
{
    public string Address { get; set; }
    public string Title { get; set; }
    public string DinnerID { get; set; }
    public string EventDate { get; set; }
    public string Country { get; set; }
    public string Latitude { get; set; }
    public string Longitude { get; set; }
    public string Description { get; set; }
    public string HostedBy { get; set; }
    public string ContactPhone { get; set; }
    public bool IsAnyoneRegistered { get; set; }
    public bool IsNobodyRegistered { get; set; }
    public bool IsCurrentUserRegistered { get; set; }
    public bool IsCurrentUserHosting { get; set; }

    public List<RsvpViewModel> RSVPs { get; set; }

    public class RsvpViewModel
    {
        public string AttendeeName { get; set; }
    }
}

The really key part is the AutoMapper configuration profile.  You can group configurations with profiles.  Maybe in one profile you format dates in one way, in another profile you format dates in another way.  I’m just using one profile here.

public class ViewModelProfile : Profile
{
    protected override string ProfileName
    {
        get { return "ViewModel"; }
    }

    protected override void Configure()
    {
        AddFormatter<HtmlEncoderFormatter>();
        ForSourceType<DateTime>().AddFormatter<StandardDateFormatter>();

        CreateMap<Dinner, DinnerDetailsViewModel>()
            .ForMember(x => x.IsCurrentUserRegistered, o => o.ResolveUsing<CurrentUserRegisteredResolver>())
            .ForMember(x => x.IsCurrentUserHosting, o => o.ResolveUsing<CurrentUserHostingResolver>())
            .ForMember(x => x.EventDate, o => o.SkipFormatter<HtmlEncoderFormatter>());

        CreateMap<RSVP, DinnerDetailsViewModel.RsvpViewModel>()
            .ForMember(x => x.AttendeeName, o => o.AddFormatter<AttendeeNameFormatter>());
    }
}

Most properties of the view model are mapped conventionally.  The property names match up so AutoMapper knows exactly what do do with them.  AutoMapper will do a lot more for you if you’d like it to.  This is actually a pretty hefty configuration.  In a different scenario it’d be likely that almost everything is mapped conventionally.

Note the first AddFormatter call.  That’s instructing AutoMapper to html encode everything.  I skip it for a property later.  The possibilities here are endless.  One cool thing we do in another project is wrap each property in a span that’s given a conventionally named CSS class.  In automated UI tests, we can use that class to find the proper element and ensure that the screen is displaying the right thing.

image

Let me know if you have any questions.

About these ads
This entry was posted in Tools and tagged , . Bookmark the permalink.

17 Responses to AutoMapper in NerdDinner

  1. Aaron says:

    Yeah, I’ve got a question – what kind of awesome sauce do you put on your Wheaties, ’cause I’d like to use the same stuff! :)
    Great write-up! It helped get a simple concrete example of AutoMapper into my head, which is something I’ve needed.

  2. Pingback: Dew Drop – July 6, 2009 | Alvin Ashcraft's Morning Dew

  3. Jon Kruger says:

    +1 for AutoMapper! Great to see this in NerdDinner.

  4. Great stuff! Thanks for doing this.

  5. Rock Ge says:

    The AutoMapper folder looks hefty for such a simple project :(

  6. Matt says:

    @Rock: Definitely. That’s why I wouldn’t really use it for NerdDinner. It’s too much overhead. But for a project of greater complexity, AutoMapper will really grease the wheels.

  7. Pingback: The Technology Post for July 6th, 2009 | I love .NET!

  8. Pingback: The Technology Post for July 6th, 2009 | Nexo IT - Information Technology News

  9. Pingback: Daily Links for Tuesday, July 7th, 2009

  10. Pingback: 9eFish

  11. Arif says:

    I am trying to run your sample project “NerdDinner” it works fine for Find Dinner menu but after click on Host Dinner menu I am getting following InvaliOperationException on :
    The model item passed into the dictionary is of type ‘NerdDinner.Models.Dinner’ but this dictionary requires a model item of type ‘NerdDinner.Models.DinnerDetailsViewModel’.
    How can I get rid of this error?

  12. Pingback: AutoMapper Introduction and Samples from NETDUG | Elegant Code

  13. Sam says:

    Does this work both ways? Can you “set” values to your mapped class?

  14. Pingback: Learning ASP.NET MVS through NerdDinner. « Implementation Details

  15. Pingback: The Big Boy MVC Series — Part 22: Mistakes Make Me Human. Fixing Mistakes Makes You Brad Wilson. « weirdlover - Asp.Net MVC Tutorials (eng)

  16. window regulators says:

    Excellent Post.. I enjoy some of content in the post.. please keep it up..
    i want same like this from you.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com 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 )

Google+ photo

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

Connecting to %s