Decoupling a strongly typed view with a View model

Now that we have seen how to work with the ViewData dictionary and strongly typed views and have learned the shortcomings of each method, let's take a look at another approach. We will now use what is called a view model to pass our data from our controller to our view.

Getting ready

This recipe will build on the code from the last recipe. In this case, we will use the Product class, as well as NBuilder, to generate some Product data for us. In this recipe, we also want to pass a Category object to our view. To do this, we will need to add one more layer of abstraction between our business layer (currently our controller) and the presentation layer (the view) using a new view model class that can hold the current Category and a Product from that Category.

How to do it...

  1. The first thing we are going to do is create a simple Category class in the Models directory.

    Models/Category.cs:

    public class Category
    {
    public string Name { get; set; }
    public int Id { get; set; }
    }
    
  2. Next we need to create a view model. Generally, a view model is named for the view that uses it. In this case, we will be passing the view model out to our Product view, so we will call this view model the ProductView (or we can call it ProductViewModel). It will be responsible for carrying our Product and Category objects. Create a new ProductView class in the Models directory.

    Models/ProductView.cs:

    public class ProductView
    {
    public Product CurrentProduct { get; set; }
    public Category CurrentCategory { get; set; }
    }
    
  3. With these two new classes created, we can open up our Product.aspx view page. We need to update it so that the view page inherits from the System.Web.Mvc.ViewPage<ProductView>.

    Views/Home/Product.aspx:

    <%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<ProductView>" %>
    <%@ Import Namespace="{project name}.Models" %>
    
  4. Next, we need to update our Product.aspx view so that, instead of trying to display the ProductName directly off of the view's Model, we instead call the Product class in the Model and then the ProductName. Then we can output the current category's name too.

    Views/Home/Product.aspx:

    <h2><%= Model.CurrentCategory.Name %></h2>
    <%= Model.CurrentProduct.ProductName %>
    
  5. Finally, we need to wire up the data that will be passed from our HomeController to our view. Do this by opening up the HomeController.cs file. Then add code to instantiate a new Category. After this, add the new Category and Product to a new instance of a ProductView object and return that ProductView instance to the view.

    Controllers/HomeController.cs:

    public ActionResult Product()
    {
    Product p = Builder<Product>
    .CreateNew()
    .Build();
    Category c = Builder<Category>
    .CreateNew()
    .Build();
    ProductView pv = new ProductView();
    pv.CurrentCategory = c;
    pv.CurrentProduct = p;
    return View(pv);
    }
    
  6. Now you can hit F5 and see your site open up and display the current category and current product.
    How to do it...

How it works...

This recipe wasn't so much about how something specifically worked, but more about explaining a specific design pattern that allows you to decouple your presentation layer away from knowing too much about your domain. Generally speaking, I would pass only view-specific objects to my view. For the most part, there is never a need for the view to know everything about a specific domain object.

Take a real life product for example; it would have name, price, and description—sure. Those are normal properties to expose to the view. But your business layer would also need to know the product's cost, weight, vendor, amount in stock, and so on. None of this information ever needs to make it out to your public site.

Also, if the view knows too much about specific domain knowledge, you will run into an issue—in that when you go to refactor your domain, you will be required to update any code referencing it. Generally speaking, you don't want information to leak across your layers (separation of concerns).

There's more...

ViewModel is not really a new pattern. You may have also heard of a Data Transfer Object or DTO. The idea of a DTO object's purpose in life is to shuttle data from one layer to another. Think of this as a contract between two layers. As long as the contract doesn't need to change, code in a specific layer can be refactored all day long with limited ripple effect throughout your code.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset