ASP.NET MVC Basics Part 2: ViewModel to Model Mapping and Editing

In Part 1, I walked through creating a simple form with a backing ViewModel and Validation. In Part 2, I’ll walk through creating a backing Model and Edit functionality.

To start off from here, load up the code from part1: https://github.com/edandersen/mvcformtutorial/tree/part1

The final code for Part 2 will be uploaded here: https://github.com/edandersen/mvcformtutorial/tree/part2

Model Mapping flow

viewmodel-model-mapping

When editing a ViewModel, we need to prepopulate the view with real data from a domain model. This can be a database table mapped to an object via an ORM or data from another source. In the diagram above, we can see the GET Edit action requesting Model with an ID of 123 from the Repository holding the model data, creating a ViewModel that represents the Model and passing it onto the View to render. POSTing the data back is similar to the Create POST method in Part 1, except we load the existing Model from the repository, update it with the validated data from the ViewModel and update the model in the Repository.

Creating the Model and Repository

Lets continue from Part 1 and create a Book Model to match the BookViewModel we already have. Again, this is a simple POCO which you can decorate with attributes. Create the following class in the Models folder:

<pre class="csharpcode"><span class="kwrd">using System;
<span class="kwrd">using System.ComponentModel.DataAnnotations;

<span class="kwrd">namespace MVCFormsExample.Models
{
    <span class="kwrd">public <span class="kwrd">class Book
    {
        [Key]
        <span class="kwrd">public Guid Id { get; set; }

        [Required]
        <span class="kwrd">public <span class="kwrd">string Name { get; set; }

        [Required]
        <span class="kwrd">public <span class="kwrd">string Author { get; set; }

        [Required]
        <span class="kwrd">public DateTime Published { get; set; }

        [Required]
        <span class="kwrd">public <span class="kwrd">int Rating { get; set; }
    }
}

Next up is a data store. In a real project you would want to put this class in another project as a shared library, but for the purposes of this exercise, create a new folder called “Repositories” and create an interface for the BookRepository called IBookRepository.cs:

<pre class="csharpcode"><span class="kwrd">using System;
<span class="kwrd">using System.Collections.Generic;
<span class="kwrd">using MVCFormsExample.Models;

<span class="kwrd">namespace MVCFormsExample.Repositories
{
    <span class="kwrd">public <span class="kwrd">interface IBookRepository
    {
        Book GetById(Guid id);
        <span class="kwrd">void Upsert(Book book);
        IEnumerable<Book> All { get; }
    }
}

This very simple interface is all we need. Lets implement an in-memory, static BookRepository. Create a file called StaticBookRepository.cs:

<pre class="csharpcode"><span class="kwrd">using System;
<span class="kwrd">using System.Collections.Concurrent;
<span class="kwrd">using System.Collections.Generic;
<span class="kwrd">using MVCFormsExample.Models;

<span class="kwrd">namespace MVCFormsExample.Repositories
{
    <span class="kwrd">public <span class="kwrd">class StaticBookRepository : IBookRepository
    {
        <span class="kwrd">private <span class="kwrd">static <span class="kwrd">readonly ConcurrentDictionary<Guid, Book> Books = <span class="kwrd">new ConcurrentDictionary<Guid, Book>();

        <span class="kwrd">public Book GetById(Guid id)
        {
            <span class="kwrd">return !Books.ContainsKey(id) ? <span class="kwrd">null : Books[id];
        }

        <span class="kwrd">public <span class="kwrd">void Upsert(Book book)
        {
            Books[book.Id] = book;
        }

        <span class="kwrd">public IEnumerable<Book> All { get { <span class="kwrd">return Books.Values; } }
    }
}

The list of Books in this Repository will only persist while the application is running (or until the AppPool is restarted) so is useless for real projects. It will however suffice for now. If you want to migrate this to an Entity Framework ORM-backed Repository later, you can reimplement IBookRepository.

Updating the Create action to save the Book

Back in the BookController, we had a TODO to save the book. Now is the time to do it, but we have to first map the validated ViewModel to the Model. Create a new method on the BookController:

<pre class="csharpcode">        <span class="kwrd">private <span class="kwrd">void UpdateBook(Book book, BookViewModel viewModel)
        {
            book.Id = viewModel.Id;
            book.Author = viewModel.Author;
            book.Name = viewModel.Name;
            book.Published = viewModel.DatePublished.GetValueOrDefault();
            book.Rating = viewModel.Rating;
        }

In real projects and definitely for larger models, you’ll want to use something like [AutoMapper](http://automapper.org/). This and other mapping methods could either be in the Controller class or as methods on the ViewModel itself. For very simple cases you can skip the ViewModel step all together and pass the Model directly to the View, but you’ll quickly find cases where this becomes impractical – in addition, if your model has public Properties that should not be set by the user, a specially crafted POST could cause the default ModelBinder to set public Properties on your Model – an “Insecure Direct Object Reference”. [Github fell foul to this problem a while ago](https://lwn.net/Articles/485325/) and was hacked due to database Models being updated directly from POST requests.

The BookController now needs access to the BookRepository. Add a new member variable to reference the newly created StaticBookRepository:

<pre class="csharpcode">    <span class="kwrd">public <span class="kwrd">class BookController : Controller
    {
        <span class="kwrd">private IBookRepository bookRepository = <span class="kwrd">new StaticBookRepository();

In most projects you’ll see the actual variable value injected in using Dependency Injection. Note how the type of the variable is an interface, so you can swap in a different BookRepository implementation and the rest of the code will be none the wiser. This is coding to contracts and you’ll need to do it to keep your code testable and modular.

Lets update the Create (POST) action to actually now save the book.

<pre class="csharpcode">        [HttpPost]
        <span class="kwrd">public ActionResult Create(BookViewModel viewModel)
        {
            <span class="kwrd">if (ModelState.IsValid)
            {
                viewModel.Id = Guid.NewGuid();
                var book = <span class="kwrd">new Book();
                UpdateBook(book, viewModel);
                bookRepository.Upsert(book);
                <span class="kwrd">return RedirectToAction(<span class="str">"Index");
            }

            <span class="kwrd">return View(viewModel);
        }

Stepping through the new code inside the ModelState.IsValid conditional, we see that we set the Id of the ViewModel to a new Guid (because it will be Guid.Zero at the moment), create a new Book model object, update the values of it from the ViewModel and Insert it into the Repository with the Upsert method. If you get creative with breakpoints, you can now see the book has been inserted into the repository but for now you won’t see anything different if you run the app.

Displaying the list of Books

To see the results of our handiwork, we have to update the Index action to show the list of Books. Updating the Index action is easy:

<pre class="csharpcode">        <span class="kwrd">public ActionResult Index()
        {
            <span class="kwrd">return View(bookRepository.All);
        }

This simply passes all the books through to the View as the view’s backing model. To display the data, update the Index view to use the new Model type (IEnumerable<Book>) and to contain a table with the data – while we are at it, we’ll include a link to the Edit action that we will create shortly:

<pre class="csharpcode">@model IEnumerable<span class="kwrd"><<span class="html">MVCFormsExample.Models.Book<span class="kwrd">>

@{
    ViewBag.Title = "Books";
}

<span class="kwrd"><<span class="html">h2<span class="kwrd">>@ViewBag.Title<span class="kwrd"></<span class="html">h2<span class="kwrd">>

@Html.ActionLink("Create", "Create")

<span class="kwrd"><<span class="html">table<span class="kwrd">>
    <span class="kwrd"><<span class="html">thead<span class="kwrd">>
        <span class="kwrd"><<span class="html">tr<span class="kwrd">>
            <span class="kwrd"><<span class="html">th<span class="kwrd">>Name<span class="kwrd"></<span class="html">th<span class="kwrd">>
            <span class="kwrd"><<span class="html">th<span class="kwrd">>Author<span class="kwrd"></<span class="html">th<span class="kwrd">>
            <span class="kwrd"><<span class="html">th<span class="kwrd">>Date Published<span class="kwrd"></<span class="html">th<span class="kwrd">>
            <span class="kwrd"><<span class="html">th<span class="kwrd">>Rating<span class="kwrd"></<span class="html">th<span class="kwrd">>
            <span class="kwrd"><<span class="html">th<span class="kwrd">></<span class="html">th<span class="kwrd">>
        <span class="kwrd"></<span class="html">tr<span class="kwrd">>
    <span class="kwrd"></<span class="html">thead<span class="kwrd">>
    <span class="kwrd"><<span class="html">tbody<span class="kwrd">>
        @foreach (var book in Model)
        {
            <span class="kwrd"><<span class="html">tr<span class="kwrd">>
                <span class="kwrd"><<span class="html">td<span class="kwrd">>@book.Name<span class="kwrd"></<span class="html">td<span class="kwrd">>
                <span class="kwrd"><<span class="html">td<span class="kwrd">>@book.Author<span class="kwrd"></<span class="html">td<span class="kwrd">>
                <span class="kwrd"><<span class="html">td<span class="kwrd">>@book.Published.ToShortDateString()<span class="kwrd"></<span class="html">td<span class="kwrd">>
                <span class="kwrd"><<span class="html">td<span class="kwrd">>@book.Rating<span class="kwrd"></<span class="html">td<span class="kwrd">>
                <span class="kwrd"><<span class="html">td<span class="kwrd">>@Html.ActionLink("Edit","Edit", new {id = book.Id})<span class="kwrd"></<span class="html">td<span class="kwrd">>
            <span class="kwrd"></<span class="html">tr<span class="kwrd">>
        }
    <span class="kwrd"></<span class="html">tbody<span class="kwrd">>
<span class="kwrd"></<span class="html">table<span class="kwrd">>

Now lets test out our new form! You will at first see no data:

01-empty-grid

Click “Create” and fill in the form:

02-sample-data

You’ll see the table populated with the Book you just created. Awesome!

03-sample-data-grid

Edit functionality

With the groundwork for Create laid down, we can reuse most of our existing code to create the Edit functionality outlined in the diagram at the top of this post. Lets create the View first, Edit.cshtml alongside Create.cshtml:

<pre class="csharpcode">@model MVCFormsExample.ViewModels.BookViewModel

@{
    ViewBag.Title = "Edit Book";
}

<span class="kwrd"><<span class="html">h2<span class="kwrd">>@ViewBag.Title<span class="kwrd"></<span class="html">h2<span class="kwrd">>

@using (Html.BeginForm())
{
    @Html.Partial("_Form")

    <span class="kwrd"><<span class="html">button <span class="attr">type<span class="kwrd">="submit"<span class="kwrd">>Edit<span class="kwrd"></<span class="html">button<span class="kwrd">>
}

You’ll notice that we have not included all of the form elements in the Edit view, and we haven’t copy and pasted all the elements from the Create view, as that would not be DRY. Lets extract the Form elements out of Create.cshtml into a new Partial view, “\_Form”. Create “\_Form.cshtml” alongside Edit.cshtml and copy/paste the form elements from Create.cshtml. Don’t forget to set the model type of the view to BookViewModel:

<pre class="csharpcode">@model MVCFormsExample.ViewModels.BookViewModel

@Html.HiddenFor(m =<span class="kwrd">> m.Id)

<span class="kwrd"><<span class="html">div <span class="attr">class<span class="kwrd">="editor-label"<span class="kwrd">>
    @Html.LabelFor(m =<span class="kwrd">> m.Name)
<span class="kwrd"></<span class="html">div<span class="kwrd">>
<span class="kwrd"><<span class="html">div <span class="attr">class<span class="kwrd">="editor-field"<span class="kwrd">>
    @Html.EditorFor(m =<span class="kwrd">> m.Name)
    @Html.ValidationMessageFor(m =<span class="kwrd">> m.Name)
<span class="kwrd"></<span class="html">div<span class="kwrd">>
<span class="kwrd"><<span class="html">div <span class="attr">class<span class="kwrd">="editor-label"<span class="kwrd">>
    @Html.LabelFor(m =<span class="kwrd">> m.Author)
<span class="kwrd"></<span class="html">div<span class="kwrd">>
<span class="kwrd"><<span class="html">div <span class="attr">class<span class="kwrd">="editor-field"<span class="kwrd">>
    @Html.EditorFor(m =<span class="kwrd">> m.Author)
    @Html.ValidationMessageFor(m =<span class="kwrd">> m.Author)
<span class="kwrd"></<span class="html">div<span class="kwrd">>
<span class="kwrd"><<span class="html">div <span class="attr">class<span class="kwrd">="editor-label"<span class="kwrd">>
    @Html.LabelFor(m =<span class="kwrd">> m.DatePublished)
<span class="kwrd"></<span class="html">div<span class="kwrd">>
<span class="kwrd"><<span class="html">div <span class="attr">class<span class="kwrd">="editor-field"<span class="kwrd">>
    @Html.EditorFor(m =<span class="kwrd">> m.DatePublished)
    @Html.ValidationMessageFor(m =<span class="kwrd">> m.DatePublished)
<span class="kwrd"></<span class="html">div<span class="kwrd">>
<span class="kwrd"><<span class="html">div <span class="attr">class<span class="kwrd">="editor-label"<span class="kwrd">>
    @Html.LabelFor(m =<span class="kwrd">> m.Rating)
<span class="kwrd"></<span class="html">div<span class="kwrd">>
<span class="kwrd"><<span class="html">div <span class="attr">class<span class="kwrd">="editor-field"<span class="kwrd">>
    @Html.EditorFor(m =<span class="kwrd">> m.Rating)
    @Html.ValidationMessageFor(m =<span class="kwrd">> m.Rating)
<span class="kwrd"></<span class="html">div<span class="kwrd">>

Also update the Create.cshtml view to use the new Partial View instead. Your project should now look like this:

04-project-layout

Test your Create action again to make sure it all still works. With the Edit view done, we can work on the Controller. First, add a method to the BookController for creating a new ViewModel from an existing Book:

<pre class="csharpcode">        <span class="kwrd">private BookViewModel ViewModelFromBook(Book book)
        {
            var viewModel = <span class="kwrd">new BookViewModel
                {
                    Id = book.Id,
                    Name = book.Name,
                    Author = book.Author,
                    DatePublished = book.Published,
                    Rating = book.Rating
                };
            <span class="kwrd">return viewModel;
        }

This is the counterpart to the UpdateBook method, mapping in the other direction. Finally, add two new Actions for Edit, one for GET and one for POST, very similar to the Create methods:

<pre class="csharpcode">        <span class="kwrd">public ActionResult Edit(Guid id)
        {
            var book = bookRepository.GetById(id);
            <span class="kwrd">if (book == <span class="kwrd">null) <span class="kwrd">return <span class="kwrd">new HttpNotFoundResult();
            <span class="kwrd">return View(ViewModelFromBook(book));
        }

        [HttpPost]
        <span class="kwrd">public ActionResult Edit(BookViewModel viewModel)
        {
            <span class="kwrd">if (ModelState.IsValid)
            {
                var existingBook = bookRepository.GetById(viewModel.Id);
                UpdateBook(existingBook, viewModel);
                bookRepository.Upsert(existingBook);
                <span class="kwrd">return RedirectToAction(<span class="str">"Index");
            }

            <span class="kwrd">return View(viewModel);
        }

Lets walk through these two methods. The first is the GET method, which gets a book by the id parameter that was mapped from the route /Edit/{id}. It then returns a 404 Not Found result if the BookRepository returned null. Finally, it returns the Edit view with a new BookViewModel containing data from the book, mapped by the ViewModelFromBook method. The POST method first checks that the ViewModel is valid using the standard Validation rules, gets the existing book from the BookRepository, updates the book values from the ViewModel, updates the book in the Repository with the Upsert method and finally redirects to the Index method.

Now time to test it! Fire up the app and create a book as before. Or create two books!

04-twobooks

Click the Edit button and the Edit (GET) action will load up. Note in the screenshot below that the Id of the Book you selected is part of the MVC route – this was mapped to the id parameter of the Edit method. Change some data:

05-bookupdate

Clicking the Edit button will update the Book model and you’ll see the updated list:

06-book-update-complete

And there you have it! You now have a very primitive in-memory database of books.

Summary

In this part of the tutorial, we looked at creating a Model, a Repository interface with a static, in-memory implementation, mapping between the ViewModel and Model, updated the Index view to show all the Books, updated the Create action to actually save the data and created an Edit action and View. Phew! In Part 3, I’ll look at creating a separate list of Authors and creating a Drop down <select> list to select from when editing a Book.