Now that you've seen the basics of how models, views, and controllers work together to provide a web application, let's look at some common scenarios, such as passing parameters and annotating models.
Back in the HomeController
class, add the following action method. It uses a class called defaultmodelbinder
to automatically match the id
passed in the route to the parameter named id
in the method.
Inside the method, we check to see whether id
is null, and if so, it returns a 404 status code and message. Otherwise, we can connect to the database and try to retrieve a product using the id
variable. If we find a product, we pass it to a view; otherwise, we return a different 404 status code and message, as shown in the following code:
public IActionResult ProductDetail(int? id) { if (!id.HasValue) { return NotFound("You must pass a product ID in the route, for example, /Home/ProductDetail/21"); } var model = db.Products.SingleOrDefault(p => p.ProductID == id); if (model == null) { return NotFound($"A product with the ID of {id} was not found."); } return View(model); // pass model to view }
Now, we need to create a view for this request.
In Visual Studio 2017, inside the Views
folder, right-click on Home and choose Add | New Item.... Choose MVC
View Page and name it ProductDetail.cshtml
.
In Visual Studio Code, inside the Views/Home
folder, add a new file named ProductDetail.cshtml
.
Modify the contents, as shown in the following markup:
@model Packt.CS7.Product @{ ViewData["Title"] = "Product Detail - " + Model.ProductName; } <h2>Product Detail</h2> <hr /> <div> <dl class="dl-horizontal"> <dt>Product ID</dt> <dd>@Model.ProductID</dd> <dt>Product Name</dt> <dd>@Model.ProductName</dd> <dt>Category ID</dt> <dd>@Model.CategoryID</dd> <dt>Unit Price</dt> <dd>@Model.UnitPrice.Value.ToString("C")</dd> <dt>Units In Stock</dt> <dd>@Model.UnitsInStock</dd> </dl> </div>
Run the web application, and when the home page appears with the list of products, click one of them, for example, product 2, Chang. The result should look something like the following screenshot:
In the HomeController
class, import the Microsoft.EntityFrameworkCore
namespace.
Add a new action method, as shown in the following code:
public IActionResult ProductsThatCostMoreThan(decimal? price) { if (!price.HasValue) { return NotFound("You must pass a product price in the query string, for example, /Home/ProductsThatCostMoreThan?price=50"); } var model = db.Products.Include(p => p.Category).Include( p => p.Supplier).Where(p => p.UnitPrice > price).ToArray(); if (model.Count() == 0) { return NotFound($"No products cost more than {price:C}."); } ViewData["MaxPrice"] = price.Value.ToString("C"); return View(model); // pass model to view }
Inside the Views/Home
folder, add a new file named ProductsThatCostMoreThan.cshtml
.
Modify the contents, as shown in the following code:
@model IEnumerable<Packt.CS7.Product> @{ ViewData["Title"] = "Products That Cost More Than " + ViewData["MaxPrice"]; } <h2>Products That Cost More Than @ViewData["MaxPrice"]</h2> <table class="table"> <tr> <th> @Html.DisplayNameFor( model => model.Category.CategoryName) </th> <th> @Html.DisplayNameFor(model => model.Supplier.CompanyName) </th> <th> @Html.DisplayNameFor(model => model.ProductName) </th> <th> @Html.DisplayNameFor(model => model.UnitPrice) </th> <th> @Html.DisplayNameFor(model => model.UnitsInStock) </th> </tr> @foreach (var item in Model) { <tr> <td> @Html.DisplayFor(modelItem => item.Category.CategoryName) </td> <td> @Html.DisplayFor(modelItem => item.Supplier.CompanyName) </td> <td> @Html.DisplayFor(modelItem => item.ProductName) </td> <td> @Html.DisplayFor(modelItem => item.UnitPrice) </td> <td> @Html.DisplayFor(modelItem => item.UnitsInStock) </td> </tr> } </table>
In the Views/Home
folder, open Index.cshtml
and add the following div
element at the bottom of the file. This will provide a form for the user to enter a price. The user can then click on a submit button to call the action method that shows only products that cost more than the entered price:
<div class="row"> <form asp-action="ProductsThatCostMoreThan" method="get"> <input name="price" placeholder="Enter a product price" /> <input type="submit" /> </form> </div>
Run the web application, and on the home page, scroll down and enter a price in the form, for example, 50
. Then, click Submit Query, as shown in the following screenshot:
You will see a table of the products that cost more than the price that you entered, as shown in the following screenshot:
You might have noted that the column headings in the table used the names of the properties by default. This means that if the property is multiple words, it won't have spaces. We can use data annotations to improve this.
In the Models
folder, open the Product
class.
Add the [Display]
attributes before each property that you want to have a different label, for example, Product
Name, Unit
Price, Units
in
Stock, and so on, like this code example:
[Display(Name = "Product Name")] public string ProductName { get; set; }
Apply the [Display]
attribute to some of the properties of the other classes, especially Category's Category Name and Supplier's Company Name.
Restart the web application.
Enter a product price and click on Submit Query.
Note that the column headings now reflect the display attributes and not the property names, as shown in the following screenshot:
18.221.254.61