Apr 14 2009

Using AutoMapper to map view models in ASP.NET MVC

Category: Uncategorizedbengtbe @ 06:27

In projects that have complex domain models it is often necessary to map the domain models to simpler objects such as Data Transfer Objects (DTO) which is “an object that carries data between processes in order to reduce the number of method calls” or Presentation Models which “represent the state and behavior of the presentation independently of the GUI controls used in the interface“.

The mapping code is often boring and tedious to write, so when I first read about AutoMapper it triggered my interest. In this post I will take a first look at AutoMapper, and show how it can be used to map a complex domain model to a view model in an ASP.NET MVC application.

About AutoMapper

The project homepage describes AutoMapper as “a fluent configuration API to define an object-object mapping strategy. AutoMapper uses a convention-based matching algorithm to
match up source to destination values. Currently, AutoMapper is geared towards model projection scenarios to flatten complex object models to DTOs and other simple objects, whose design is better suited for serialization, communication, messaging, or simply an anti-corruption layer between the domain and application layer.

AutoMapper is a fairly new project; the current version is 0.30 Beta, and it seems to be in active development.

A complex domain model

The domain model that I’m going to use in this example is a Customer class that has a reference to an Address class:

public class Customer

{

    public string FirstName { get; set; }

    public string LastName { get; set; }

    public string Email { get; set; }

    public Address HomeAddress { get; set; }

    public string GetFullName()

    {

        return string.Format(“{0} {1}”, FirstName, LastName);

    }

}

The Address class is as follows:

public class Address

{

    public string Address1 { get; set; }

    public string Address2 { get; set; }

    public string City { get; set; }

    public string PostalCode { get; set; }

    public string Country { get; set; }

}

Ok, I admit that this domain model is not very complex, but it will serve the purpose of this post :)

The simple view model

The view is going to show a list of customers with name, email and country. We therefore create a view model called CustomerListViewModel:

public class CustomerListViewModel

{

    public string FullName { get; set; }

    public string Email { get; set; }

    public string HomeAddressCountry { get; set; }

}

I save this view model in the file ViewModel/Customers/CustomerListViewModel.cs in the web-project. In an ASP.NET MVC application the view models belong to the Web-project, they are not part of the domain model. Usually a view model belongs to a single controller, and its views.

The controller

The URL used to display the customers should be http://application/Customers/, hence the name of the controller is CustomersController, and the action used is the Index():

public class CustomersController : Controller

{

    private readonly ICustomerService m_CustomerService;

    public CustomersController(ICustomerService customerService)

    {

        m_CustomerService = customerService;

    }

    // GET: /Customers/

    public ActionResult Index()

    {

        IList<Customer> customers = m_CustomerService.GetCustomers();

        Mapper.CreateMap<Customer, CustomerListViewModel>();

        IList<CustomerListViewModel> viewModelList =

           Mapper.Map<IList<Customer>, IList<CustomerListViewModel>>(customers);

        return View(viewModelList);

    }

}

In the above controller I use Dependency Injection to inject the CustomerService in the constructor. In a previous post I have shown how to use Dependency Injection in the controllers.

When using AutoMapper you first have to create a map between the two classes:

Mapper.CreateMap<Customer, CustomerListViewModel>();

The following line maps the list of Customers to a list of CustomerListViewModel:

IList<CustomerListViewModel> viewModelList =

   Mapper.Map<IList<Customer>, IList<CustomerListViewModel>>(customers);

If you just wanted to map a single instance you would use:

CustomerListViewModel viewModel = Mapper.Map<Customer, CustomerListViewModel>(customer);

AutoMapper will perform the following mapping automatically:

  • FullName gets its value by calling the Customer.GetFullName() method.
  • Email gets its value from the Customer.Email propery.
  • HomeAddressCountry gets its value from the Customer.HomeAddress.Country property.

The view

Below is the view used to display the view model:

<%@ Page Title=”" Language=”C#” MasterPageFile=”~/Views/Shared/Site.Master”

       Inherits=”System.Web.Mvc.ViewPage<IEnumerable<CustomerListViewModel>>” %>

<%@ Import Namespace=”MyApp.Web.ViewModels.Customers”%>

<asp:Content ID=”Content1″ ContentPlaceHolderID=”MainContent” runat=”server”>

    <h2>Customer list</h2>

    <table>

        <tr>

            <th>Name</th>

            <th>Email</th>

            <th>Country</th>

        </tr>

    <% foreach (var customer in Model) { %>

        <tr>

            <td><%= Html.Encode(customer.FullName) %></td>

            <td><%= Html.Encode(customer.Email) %></td>

            <td><%= Html.Encode(customer.HomeAddressCountry) %></td>

        </tr>

    <% } %>

    </table>

</asp:Content>

<asp:Content ID=”Content2″ ContentPlaceHolderID=”head” runat=”server”></asp:Content>

As you can see from the Inherits attribute, this view is strongly typed to the IEnuerable<CustomerListViewModel>. The foreach uses the Model property to retrieve the list of CustomerListViewModel, and creates a table row for each instance.

Some advanced features of AutoMapper

In the example above I have shown some simple usages of AutoMapper. I will now take a quick look at some of its more advanced features:

Let’s say that the Customer has a Birthday property of type DateTime that we want to map to a YearOfBirth property of type int:

Mapper.CreateMap<Customer, CustomerListViewModel>()

    .ForMember(dest => dest.YearOfBirth, opt => opt.MapFrom(src => src.Birthday.Year));

Don’t get confused by all the lamdas (=>). The ForMember method just says YearOfBirth should get its value from Birthday.Year.

AutoMapper can also help you test the mapping:

[Test]

public void Should_be_able_to_map_Customer_to_CustomerListViewModel()

{

    Mapper.CreateMap<Customer, CustomerListViewModel>()

        .ForMember(dest => dest.ThisIsMappedElsewhere, opt => opt.Ignore());

    Mapper.AssertConfigurationIsValid();

}

In the above test AutoMapper will check to make sure that every single member on CustomerListViewModel has a corresponding member on the Customer, except for the property ThisIsMappedElsewhere that we told it to ignore.

There are many more features that I haven’t covered. You can find more information in the following places:

kick it on DotNetKicks.com Shout it

Tags: ,