Files
popcyclical-blog-archive/posts/2010-01-04-aspnet-mvc-viewmodel-value-formatting-using-automapper.md
T

104 lines
5.0 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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. Well 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. Heres 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 thats available today in MVC Futures or the RC.  Check out *[*Matts 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 ViewModels 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 [DataAnnotationss 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.