104 lines
5.0 KiB
Markdown
104 lines
5.0 KiB
Markdown
---
|
||
title: "ASP.NET MVC ViewModel Value Formatting using AutoMapper"
|
||
date: 2010-01-04T19:49:33.31-06:00
|
||
slug: aspnet-mvc-viewmodel-value-formatting-using-automapper
|
||
published: true
|
||
---
|
||
|
||
From [ASP.NET MVC in Action](http://www.manning.com/palermo/) 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](http://www.codeplex.com/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](http://www.codeplex.com/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)%>
|
||
<%} %>
|
||
</asp:Content>
|
||
|
||
*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*](http://www.trycatchfail.com/blog/post/2009/12/11/My-best-%28or-worst%29-MVC-hack-to-datehellip3b.aspx)* 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:[AcceptVerbs(HttpVerbs.Post)]
|
||
public ActionResult Index(ViewModel viewModel)
|
||
{
|
||
var model = new DomainModel();
|
||
|
||
viewModel.MapTo(model);
|
||
|
||
// ... 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*](/2010/01/01/jQueryValidateAndJeditablePart1.aspx)*.*
|
||
|
||
… 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
|
||
.ResolveUsing<CurrencyResolver>()
|
||
.FromMember(vm => vm.CurrencyProperty));
|
||
|
||
This utilizes another [AutoMapper](http://www.codeplex.com/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);
|
||
}
|
||
}
|
||
|
||
#### Conclusions
|
||
|
||
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](http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.displayformatattribute.aspx) to decorate the Model or ViewModel and the framework automagically applying the formatting while rendering the View. |