ASP.NET Core 2.2 For Beginners (Part 5): Models

In this chapter, you will learn more about different types of model classes in the MVC framework and how to use them.

Up until now, you have used the Video class as a model for the Index view. In simple solutions that might be fine, but in more complex solutions, you need to use entity models and view models. Sometimes you even make a more granular distinction between the models, using Data Transfer Objects (DTOs) with the view models.

An entity model is typically used to define a table in a database. A view model is used to transport data from the controller to the view, but sometimes the view model needs to contain objects, and that’s where the DTOs come into play. Some might argue that DTOs are view models, and in some scenarios they are.

You will create a new folder called Entities and move the Video class to that folder. The reason for moving the file is that the Video class later will be used to define a table in a SQL Server database. A class used to define a database table is referred to as an entity. You will also add a new folder named ViewModels, which will hold the view models creat­ed throughout the first part of the book.

Important to note is that the view model typically contains other data than the entity model, although some properties are the same. One example is when a video is being added in a Create view. The view model needs some properties from the entity model, but could also need other information that is not stored in the Video entity and must be fetched from another database table, or an enum.

A view model is never used to directly update the database. To update the database the data from the view model is added to an entity model, which then in turn updates the database table.

Let’s look at an example where an enum is used to display the genre a video belongs to. For simplicity, a video can only belong to one genre.

JQuery: You will use JQuery and JQuery Validation to perform client-side validation. This will make sure that the user input is conforming to the validation rules before the data is sent to the server action. Some validation rules are added by the framework; others you have added yourself to the entity and view model classes as attributes. Examples of valida­tion rules are: password restrictions set by the framework (can be changed), and the Required, MaxLength, and DataType attributes.

View Model Example

First you need to add an Entities folder and a ViewModels folder to the folder structure.

Changing the Folder Structure

  1. Create a new folder called Entities in the project.
  2. Move the cs file from the Models folder to the Entities folder, using drag-and-drop.
  3. Open the Video class and change the Models namespace to Entities.

namespace AspNetCore22Intro.Entities

  1. Open the MockVideoData class and change the using statement from Models namespace to Entities.
  2. Open the IVideoData interface and change the using statement from Models namespace to Entities.
  3. Open the Index view and change the Models namespace to Entities for the @model
  4. Open the HomeController class and remove any unused using

Adding the View Model

  1. Add a class called Genres to the Models
  2. Replace the class keyword with the enum keyword and add some genres.

public enum Genres
{
    None,
    Horror,
    Animated,
    Comedy,
    Romance,
    Action
}

  1. Open the Video class and add an int property called GenreId to the class. This will hold the enum value for the video’s genre.

public class Video
{
    public int Id { get; set; }
    public string Title { get; set; }
    public int GenreId { get; set; }
}

  1. Open the MockVideoData class and add a genre id for each video.

new Video { Id = 3, GenreId = 2, Title = "Megamind" }

  1. Add a new folder called ViewModels to the project.
  2. Add a class called VideoViewModel to the ViewModel
  3. The view model will contain the Id and Title properties, but you don’t want to display the genre id; it would be nicer to display the actual genre. To achieve this, you add a string property called Genre to the VideoViewModel class, but not to the Video

public class VideoViewModel
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Genre { get; set; }
}

Using the View Model

Now that the view model has been created, you need to send it to the view as its model. This requires some changes to the HomeController class and the Index view. You need to fetch the video from the _videos collection using its id, and then convert the genre id to the name for the corresponding value in the Genres enum.

When the view model has been assigned values from the entity object and the enum name, it is sent to the view with the View method.

  1. Open the HomeController
  2. Add a using statement to the Linq namespace to get access to the Select method and the System namespace to get access to the Enum class. Also, add a using statement to the ViewModels and Models namespaces to get access to the genre enum and the view model you added.

using AspNetCore22Intro.Models;
using AspNetCore22Intro.ViewModels;
using System;
using System.Linq;

  1. Use the LINQ Select method in the Index action to convert each video into a VideoViewModel object, and store it in the model Use the Enum.GetName method to fetch the genre corresponding to the video’s genre id.

public ViewResult Index()
{
    var model = _videos.GetAll().Select(video =>
        new VideoViewModel
        {
            Id = video.Id,
            Title = video.Title,
            Genre = Enum.GetName(typeof(Genres), video.GenreId)
        });
    return View(model);
}

  1. Open the Index view and change the @model directive to an IEnumerable<VideoViewModel>.

@model IEnumerable<AspNetCore22Intro.ViewModels.VideoViewModel>

  1. Add a new <td> for the genre.

<td>@video.Genre</td>

  1. Switch to the browser and refresh the application. As you can see, the genres are now displayed beside each of the video titles.

Adding a Details View

Now that you know how to use the VideoViewModel to send data to the Index view, it is time to add a new view to the Views folder.

The Details view will display a single video in the browser, based on the id sent to the Details action you will add next.

  1. Add a new public action method called Details to the HomeController. It should have an int parameter named id, which will match a video id from the URL or the request data. The return type should be IActionResult, which makes it possible to return different types of data.

public IActionResult Details(int id)
{
}

  1. To fetch the video matching the passed-in id, you must add a new method called Get to the IVideoData The method should have an int parameter called id and return a video object.

Video Get(int id);

  1. Now you need to implement that method in the MockVideoData class, and have it return the video matching the id parameter value. Use LINQ to fetch the video with the FirstOrDefault

public Video Get(int id)
{
    return _videos.FirstOrDefault(v => v.Id.Equals(id));
}

  1. Add a using statement to the Linq namespace.

using System.Linq;

  1. Add a variable called model to the Details action in the HomeController Call the Get method to fetch the videos matching the passed-in id and assign them to the model variable.

var model = _videos.Get(id);

  1. To test the Get method, return the model variable using an ObjectResult

return new ObjectResult(model);

  1. Save all files and switch to the browser. Navigate to the /Home/Details/2 The video matching the id 2 should be displayed.
  2. Change the return statement in the Details action to return the View method and pass in an instance of the VideoViewModel class filled with data from the model

return View(
    new VideoViewModel
    {
        Id = model.Id,
        Title = model.Title,
        Genre = Enum.GetName(typeof(Genres), model.GenreId)
    }
);

  1. Add a new Razor View file called cshtml to the Views/Home folder in the Solution Explorer.
  2. Delete all content in the Details
  3. Type html and hit Tab to add the HTML skeleton to the view.
  4. Add the @model directive for a single VideoViewModel to the view. This enables the view to display information about one video.

@model AspNetCore22Intro.ViewModels.VideoViewModel

  1. Add the text Video to the <title> element.
  2. Add a <div> element inside the <body> element for each property.

<div>Id: @Model.Id</div>
<div>Title: @Model.Title</div>
<div>Genre: @Model.Genre</div>

  1. Save all the files and switch to the browser and refresh. You should see the data for the video matching the id in the URL.
  2. Change the id in the URL and make sure that the correct film is displayed.
  3. Change to an id that doesn’t exist. An error should be displayed in the browser. The reason for the error is that the Get method will return null if the video doesn’t exist, and the view can’t handle null values for the model.
  4. One way to solve this is to redirect to another action; in this case the Index action is appropriate. Add an if-statement above the previous return statement, which checks if the model is null; if it is, redirect to the Index Implementing this check will ensure that the action won’t show the error message, and instead display the video list.

if (model == null)
    return RedirectToAction("Index");

  1. Switch to the browser and refresh. The Index view should be displayed.
  2. Let’s add a link to the Index view in the Details view, for easy navigation back to the root. You can use a traditional HTML <a> tag, or you can use the Razor ActionLink HTML helper method. There is a new, third way to add a link, using Tag Helpers. You will explore Tag Helpers shortly.

@Html.ActionLink("Home", "Index")

  1. Switch to the browser and navigate to /Home/Details/2 The view should have a link with the text Home. Click the link to get back to the Index view.
  2. Now open the Index view and add links for the video ids. To achieve this, you must pass in an anonymous object as an extra parameter, and add an id property to that object.

<td>@Html.ActionLink(video.Id.ToString(), "Details", new { id = video.Id })</td>

  1. Switch to the browser and go to the root (/). Click one of the links to view the details for that video.

  

The complete markup for the Index view:

@model IEnumerable<AspNetCore22Intro.ViewModels.VideoViewModel>

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Video</title>
</head>
<body>
    <table>
        @foreach (var video in Model)
        {
            <tr>
            <td>@Html.ActionLink(video.Id.ToString(), "Details",
                    new { id = video.Id })</td>
            <td>@video.Title</td>
            <td>@video.Genre</td>
        </tr>
        }
    </table>
</body>
</html>

 

The complete markup for the Details view:

@model AspNetCore22Intro.ViewModels.VideoViewModel

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Video</title>
</head>
<body>
    <div>Id: @Model.Id</div>
    <div>Title: @Model.Title</div>
    <div>Genre: @Model.Genre</div>
    @Html.ActionLink("Home", "Index")
</body>
</html>

 

The complete markup for the Details action:

public IActionResult Details(int id)
{
    var model = _videos.Get(id);

    if (model == null) return RedirectToAction("Index");

    return View(new VideoViewModel
    {
        Id = model.Id,
        Title = model.Title,
        Genre = Enum.GetName(typeof(Genres), model.GenreId)
    });
}

 

Stay connected with news and updates!

Join our mailing list to receive the latest news and updates from our team.
Don't worry, your information will not be shared.

Subscribe
Close

50% Complete

Two Step

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.