Data is the heart of every application. A user enters data into the application, edits the entered data, and searches the data. We can even say that an application that we build is just an interface for the operations that we perform on the application data. So, it is absolutely necessary for any framework to provide a mechanism to handle data operations easier and more manageable. Models in ASP.NET MVC are used to represent the business domain data.
In this chapter, you'll be learning about the following topics:
ViewModels
Models are simple POCO (Plain Old C# Objects) classes representing your business domain data. For an e-commerce business, model classes would be Product
, Order
, and Inventory
. If you are building an application for a university, model classes would be Student
, Teacher,
and Subject
. Models represent the business domain data in your application and they are not aware of the underlying database that is being used in your application. In fact, you don't even need a database to work with models.
They can represent the data stored in an XML file or CSV file or any other data in your application. Having said that, these models could be used to interact with your database (in most cases) but they don't have any dependency to the database.
The following steps describe how to create an ASP.NET Core application that uses Models:
NuGet
package and configure this, as discussed in an earlier chapter.Controllers
folder and create a HomeController
with a single Index
action method.Views
model:Views
: This folder is inside your project.Views\_ViewStart.cshtml
: This identifies the name of the Layout
file.ViewsShared
folder: This folder holds all the shared View components for your application.Shared\_Layout.cshtml
: This file identifies what your web application structure should look like.ViewsHome
folder: This folder contains all of the Views of your HomeController
.ViewsHomeIndex.cshtml
: This is the view corresponding to the Index
action method of HomeController
.Now, we have created an ASP.NET Core application with Controllers and Views.
Let us create a Models
folder in our application; this folder will contain all of your model files. In a real world application, this folder and the respective model files would reside in separate projects. For the sake of simplicity, we are keeping the Models
folder and its files in the same project.
Let us create a simple model class Product
model, in the Models
folder:
public class Product { public int ProductId { get; set; } public string Name { get; set; } public decimal Price { get; set; } }
This Product
model class is no different from any other C# class and contains a few properties about the product.
Update the Index
action method in HomeController
to use the Product
model, as shown in the following code snippet. We are building the model data and passing the model data to the View so that it can be shown to the users. However, it is NOT recommended to build the Model data in the controller's action methods as it violates the separation of concerns. For the sake of simplicity only, we are building the Model data in an action method.
public IActionResult Index() { /* Build the products model. It is NOT RECOMMENDED to build models in Controller action methods like this. In real world appication, these models and the respective Data Access Layer(DAL) would be in separate projects. We are creating it here to make things simpler to explain */ List<Product> Products = new List<Product> { new Product { Name = "Mobile Phone", Price = 300 }, new Product { Name = "Laptop", Price = 1000 }, new Product { Name = "Tablet", Price = 600 } }; return View(Products); }
Update the corresponding Index
View method to use the Model data loop through each product and show it as an unordered list item. The @model
in the first line represents the Model metadata; the type of data being passed to the View. The Model in the for…each
loop represents the actual data itself, a list of products in our case:
@model List<Chapter5.Models.Product> <ul> @foreach (var Product in Model) { <li>@Product.Name</li> } </ul>
When you run the application, you'll get the following output:
We have successfully created a Model and have used it in our Controller and View.
Let us create a comparatively complex Model class, Order
(Order.cs
in the Models
folder), which contains a list of products and their total amount:
public class Order { public int OrderId { get; set; } public List<Product> Products { get; set; } public decimal Total { get; set; } }
Now, we have to update the Index
action method to use the Order
model. Once we build the list of products, we are assigning the products list to the Order
property and calculating the total cost of the order. These calculations would usually be done as part of the business layer. Again, for the sake of simplicity, we are building the data Model and calculations here in the action; this should never be the case in a real world application.
The code highlighted in bold is the changes that we have made in the action method:
public IActionResult Index() {
/* Build the products model. It is NOT RECOMMENDED to build models in Controller action methods like this. In real world appication, these models and the respective Data Access Layer(DAL) would be in separate projects. We are creating it here to make things simpler to explain */
List<Product> Products = new List<Product> {
new Product {
Name = "Mobile Phone",
Price = 300
},
new Product {
Name = "Laptop",
Price = 1000
},
new Product {
Name = "Tablet",
Price = 600
}
};
Order order = new Order();
order.Products = Products;
order.Total = Products.Sum(product => product.Price);
return View(order);
}
The View is updated to accommodate the Model changes. Model metadata (@model
) is changed to indicate that the Order information is passed to the View instead of the list of products.
Then, we are showing the list of products in table format. Please note that all of the Model data (Order
object and its properties, in this case) can be accessed through the Model. For example, the Products
class can be accessed through Model.Products
and the value of the Total
can be obtained through Model.Total
:
@model Chapter5.Models.Order <table border="1"> <tr> <th>Product Name</th> <th>Price</th> </tr> @foreach (var Product in Model.Products){ <tr> <td>@Product.Name</td> <td>@Product.Price</td> </tr> } <tr> <td><b>Total</b></td> <td><b>@Model.Total</b></td> </tr> </table>
When you run the application, you'll see the following output:
There are scenarios where you might want to update only a few properties in a large Model or you might want to create a new Model based on a few models. In such scenarios, it is better to create a new Model specific to the View.
For example, let us assume that we are building a screen where you update the price of the product. This simple screen may contain only three properties—product ID, product name, and price of the product. But the product's Model may contain more than 30 properties to hold all details of the product such as manufacturer, color, size, and other properties. Instead of sending the complete Model with all the properties, we can create a new Model specific to this view with only a few properties—ID, Name, and Price.
The ViewModels are entities where when you update the Model, your View would get updated automatically and vice versa. In many online articles and even in some books, they are referring to ViewModels when they are actually trying to mean Models specific to the View.
In ViewModels, binding is two ways—when you update either the Model or the View, the other one would get updated automatically. Let us consider a simple example; you have a form with various fields on the left-hand side and print preview on the right side. In this case, whatever you type in real time in the form will be reflected immediately on the right side. In such cases, you can use pure View models when you type, your ViewModel would be updated and that ViewModel
would be consumed in the right-hand side print preview. These pure ViewModels are being used in advanced JavaScript frameworks such as Knockout or AngularJS.
In Models specific to the View, we are binding in only one way from the Model to the View. Here, we are sending a Model specific to the View instead of the generic Model (which represents a business domain class).
However, in this book, we will be referring to Models specific to View as ViewModel
for brevity. Unless otherwise specified, you should read all ViewModels as Models specific to View. So, I am making the same mistake made by other authors .
The following block diagram shows the data flow in an ASP.NET MVC application:
Data Source represents your application data. The application data could reside anywhere—from full-fledged RDBMS such as SQL servers to simple Excel spreadsheets, or anything in between.
Models represent the business domain data for your application and are independent of the data source being used. The same model could be used with different data sources.
We can use the Model as-is in our views to get data or to present it. But in some views, you might not need all the properties of the model. So, instead of sending the entire Model to the View, we create models specific to the View and use them in our View. This makes things simpler.
The following is the high-level sequence of events that happens when you store or retrieve a record in ASP.NET Core using the Model:
Till now, we have been handling only in-memory data in our application. In almost all real world applications, some form of the database will be used for data storage, access, and retrieval. In the next section, we will discuss the Entity Framework (ORM framework), which makes data access simpler from a .NET application.