Monday, January 4, 2010

ASP.NET MVC ViewModel Value Formatting using AutoMapper

From ASP.NET MVC in Action section 4.4.1:

Views are difficult to unit test, so we want to keep them as thin as possible. … Notice in [the View Model] that all of the properties are strings. We’ll have the [properties] properly formatted before this view model object is placed in view data. This way, the view need not consider the object, and it can format the information properly.

To facilitate the formatting between the Domain Model and the View Model, a few of AutoMapper’s features may be utilized. Here’s a DomainModel containing a CurrencyProperty which will needed to formatted for human consumption:

public class DomainModel
    public decimal CurrencyProperty { get; set; }

Now, here is a ViewModel which will be used to transport the formatted value to the View:

public class ViewModel
    ///<summary>Currency Property - formatted as $#,###.##</summary>
    public string CurrencyProperty { get; set; }

Mapping from Domain Model to View Model

AutoMapper provides an easy way to create a mapping between two object types.  For particular tweaks for individual property mappings, the ForMember method can be used like:

///<summary>Setup mapping between domain and view model</summary>
static ViewModel()
    // map dm to vm
    Mapper.CreateMap<DomainModel, ViewModel>()
      .ForMember(vm => vm.CurrencyProperty, mc => mc.AddFormatter<CurrencyFormatter>());

This sets up a mapping between the DomainModel and ViewModel and additionally applies a custom formatter for CurrencyProperty.  The formatter must implement the IValueFormatter interface like so:

public class CurrencyFormatter : IValueFormatter
    ///<summary>Formats source value as currency</summary>
    public string FormatValue(ResolutionContext context)
        return string.Format(CultureInfo.CurrentCulture, "{0:c}", context.SourceValue);

…and a simple conversion constructor on the ViewModel:

/// <summary> Creates the view model from the domain model.</summary>
public ViewModel(DomainModel domainModel)
    Mapper.Map(domainModel, this);

Now, neither the Controller or View need concern about any formatting and can stay focused on orchestrating and layout:

public ViewResult Index()
    var model = new DomainModel{CurrencyProperty = 19.95m};

    var viewModel = new ViewModel(model);

    return View(viewModel);
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<ViewModel>" %>
<asp:Content ContentPlaceHolderID="MainContent" runat="server">
<% using(Html.BeginForm()) {%>
    <%= Html.TextBoxFor(m=>m.CurrencyProperty)%>
<%} %>

Aside: TextBoxFor is an upcoming ASP.NET MVC 2 feature that’s available today in MVC Futures or the RC.  Check out Matt’s post for some neat stuff.

Mapping from View Model back to Domain Model

So now the formatted value is being rendered – but how do we go about the reverse trip back to the server?  First, to define an action:

public ActionResult Index(ViewModel viewModel)
    var model = new DomainModel();

    // ... return a view or action result

Aside: for validating that what the user enters is in the correct format, see another post about jQuery Validate here.

… and a Map method on the ViewModel:

public void MapTo(DomainModel domainModel)
    Mapper.Map(this, domainModel);

But this mapping will fail without first creating a definition back from the ViewModel to the Model.  In the ViewModel’s static constructor:

  // from vm to dm
  Mapper.CreateMap<ViewModel, DomainModel>()
   .ForMember(dm => dm.CurrencyProperty, 
    mc => mc
     .FromMember(vm => vm.CurrencyProperty));

This utilizes another AutoMapper method - ResolveUsing - which can be used to get the string property back to a decimal.  The ValueResolver<TSource,TDestination> is defined like so:

public class CurrencyResolver : ValueResolver<string, decimal>
    ///<summary>Parses source value as currency</summary>
    protected override decimal ResolveCore(string source)
        return decimal.Parse(source, NumberStyles.Currency, CultureInfo.CurrentCulture);


There may be more elegant ways to accomplish formatting for MVC Views, but this method is quite workable.  In particular, I can imagine utilizing DataAnnotations’s DisplayFormatAttribute to decorate the Model or ViewModel and the framework automagically applying the formatting while rendering the View.