使用数据
第一次使用数据时,你将重点关注于使用 BakeryContext
检索要在主页和订购页上显示的数据, 这些数据还没有添加到应用程序中。 提醒一下,主页应该类似于此处的 ASP.NET Web Pages 版本:
所有产品的描述、图片和价格一起显示,随机选择其中一个产品作为特色产品出现在页面的顶部。
管理数据的显示需要少量的准备工作。首先,将以下代码添加到位于 wwwroot/css
中的现有 site.css
文件中:
body{ ?color: #696969;}a:link { ?color: #3b3420; ?text-decoration: none;}a:visited { ?color: #3b3420; ?text-decoration: none;}a:hover { ?color: #a52f09; ?text-decoration: none;}a:active { ?color: #a52f09;}a.order-button, a.order-button:hover{ ?color: #fdfcf7;}.productInfo, .action{ ?max-width: 200px;}p{ ?font-size: 0.8rem;}#orderProcess { ?list-style: none; ?padding: 0; ?clear: both;}#orderProcess li { ?color: #696969; ?display: inline; ?font-size: 1.2em; ?margin-right: 15px; ?padding: 3px 0px 0px 5px;}.step-number { ???background-color: #edece8; ???border: 1px solid #e6e4d9; ???font-size: 1.5em; ???margin-right: 5px; ???padding: 3px 10px;}.current .step-number { ???background-color: #a52f09; ???border-color: #712107; ???color: #fefefe;} .orderPageList{ ???padding-inline-start: 20px;}.actions .order-button{ ?margin-left:20px;}
PageModel
现在, 打开 Pages/Index.cshtml.cs 文件, 并替换为以下的内容:
using System;using System.Collections.Generic;using System.Linq;using System.Threading.Tasks;using Bakery.Data;using Bakery.Models;using Microsoft.AspNetCore.Mvc;using Microsoft.AspNetCore.Mvc.RazorPages;using Microsoft.EntityFrameworkCore;namespace Bakery.Pages{ ???public class IndexModel : PageModel ???{ ???????private readonly BakeryContext db; ?????????public IndexModel(BakeryContext db) => this.db = db; ???????public List<Product> Products { get; set; } = new List<Product>(); ?????????public Product FeaturedProduct { get; set; } ?????????public async Task OnGetAsync() ???????{ ???????????Products = await db.Products.ToListAsync(); ???????????FeaturedProduct = Products.ElementAt(new Random().Next(Products.Count)); ???????} ???}}
这是 PageModel 文件。 PageModel 充当页面控制器和视图模型的组合。 作为控制器, 它的角色是处理来自请求的信息, 然后为视图准备一个模型(视图模型)。 页面模型(PageModel)和内容页面(视图)存在一对一的映射, 因此页面模型(PageModel)本身就是视图模型(viewmodel)。
来自请求的信息在处理程序方法(handler methods)中处理。这个 PageModel 有一个处理方法 - OnGetAsync
,它是由使用 GET 谓词发出的 HTTP 请求按照约定执行的。 该 PageModel 有一个名为 db
的 BakeryContext
类型私有字段。它还有一个接受 BakeryContext 对象作为参数的构造方法。参数值由依赖注入系统提供。 这种模式称为构造注入。 该参数被分配给构造函数中的私有字段(使用表达式主体)。
该 PageModel 类有两个公共属性 - 一个产品列表和一个表示显示在页面顶部的特色产品的单个产品。 该列表由 OnGetAsync
方法中的以下代码填充:
Products = await db.Products.ToListAsync();
OnGetAsync
方法中的下一行代码将其中一个产品随机分配给 FeaturedProduct
属性:
FeaturedProduct = Products.ElementAt(new Random().Next(Products.Count));
内容页面
现在是生成UI的时候了。 将 Index 内容页面(Pages/Index.cshtml)的内容替换为以下的代码:
@page@model IndexModel@{ ???ViewData["Title"] = "Home page";}<h1>Welcome to Fourth Coffee!</h1><div id="featuredProduct" class="row"> ????<div class="col-sm-8"> ???????<img alt="Featured Product" src="~/Images/Products/@Model.FeaturedProduct.ImageName" class="img-fluid rounded"/> ???</div> ???<div id="featuredProductInfo" class="col-sm-4"> ???????<div id="productInfo"> ???????????<h2>@Model.FeaturedProduct.Name</h2> ???????????<p class="price">$@string.Format("{0:f}", Model.FeaturedProduct.Price)</p> ???????????<p class="description">@Model.FeaturedProduct.Description</p> ???????</div> ???????<div id="callToAction"> ???????????<a class="btn btn-danger order-button" asp-page="/order" asp-route-id="@Model.FeaturedProduct.Id" title="Order @Model.FeaturedProduct.Name">Order Now</a> ???????</div> ???</div></div><div id="productsWrapper" class="row">@foreach (var product in Model.Products){ ???<div class="col-sm-3"> ???????<a asp-page="/order" asp-route-id="@product.Id" title="Order @product.Name"> ???????????<div class="productInfo"> ???????????????<h3>@product.Name</h3> ???????????????<img class="product-image img-fluid img-thumbnail" src="~/Images/Products/Thumbnails/@product.ImageName" alt="Image of @product.Name" /> ???????????????<p class="description">@product.Description</p> ???????????</div> ???????</a> ???????<div class="action"> ???????????<p class="price float-left">$@string.Format("{0:f}", product.Price)</p> ???????????<a class="btn btn-sm btn-danger order-button float-right" asp-page="/order" asp-route-id="@product.Id" title="Order @product.Name">Order Now</a> ???????</div> ???</div>}</div>
页面顶部的 @model
指令指定页面模型(IndexModel)的类型。 您可以通过内容页面的 Model
属性使用 PageModel
。
HTML 的顶部显示了特色产品。 底部部分循环遍历所有产品并显示它们的缩略图。 每个产品都包含一个超链接,样式类似于按钮(使用 Bootstrap 样式)。 虽然它还没有实质的链接目标, 但是超链接是由一个锚标记助手(anchor tag helper)生成的, 其中包括一个 asp-route
属性。 此属性用于将数据作为路由值传递到目标页 。添加 Order 页面之后就可以看到它是如何工作的了,当然了,那个是下一步要做。
同时,通过在终端上执行 dotnet run
来测试应用程序,然后浏览到 https://localhost:5001
。主页应该是这样的:
移动用户
原始的 Web Pages Bakery 模板使用设备检测或浏览器嗅探来满足移动用户的需求。如果检测到用户使用移动设备(主要是从user-agent报头中找到的值推断出来的),则站点将切换到使用不同的布局文件(sitelayout.mobile.cshtml)。这种方法有两个问题:首先,您需要使您的设备检测库保持最新,否则它的失败可能比成功更多;其次,您需要维护站点布局文件和样式表的多个版本。
如今,处理不同设备分辨率问题的解决方案是使用响应式设计(Responsive Design), 这种技术可以检测可用的屏幕大小,并相应地对内容进行回流。这个功能内置在Bootstrap中,您可以通过调整当前打开的浏览器的大小来了解它是如何工作的。当浏览器窗口宽度降至576px以下时,显示会发生巨大变化:
添加 Order 页面
在终端中执行以下命令添加一个新页面:
dotnet new page --name Order --namespace Bakery.Pages --output Pages
打开刚刚新创建的 Order.cshtml.cs 文件并替换其内容为以下代码:
using System;using System.Threading.Tasks;using Bakery.Data;using Bakery.Models;using Microsoft.AspNetCore.Mvc;using Microsoft.AspNetCore.Mvc.RazorPages;namespace Bakery.Pages{ ???public class OrderModel : PageModel ???{ ???????private BakeryContext db; ???????public OrderModel(BakeryContext db) => this.db = db; ???????[BindProperty(SupportsGet =true)] ???????public int Id { get; set; } ???????public Product Product { get; set;} ???????public async Task OnGetAsync() => ?Product = await db.Products.FindAsync(Id); ???}}
同样,BakeryContext
被注入到 PageModel 构造函数中。 公共属性 Product 在 OnGetAsync 方法中实例化。 FindAsync 方法接受一个值, 该值表示要返回的实体的主键。 在这种情况下, 传递给 FindAsync 方法的参数是另一个公共属性 - Id。 但是它获取的值从哪里来呢?
Id 属性(property,类的属性)使用 BindProperty属性(attribute,注解用的属性)进行修饰。该属性(attribute)确保类的属性(property)包含在模型绑定过程中,这将导致作为HTTP请求的一部分传递的值映射到 PageModel 属性和处理程序方法参数。 默认情况下, 模型绑定只对 POST
请求中传递的值有效。 通过单击主页上的链接可以到达 Order 页面, 这将导致 GET
请求。您必须添加 SupportsGet = true
来选择对 GET
请求进行模型绑定。
如果您还记得的话,链接到 Order 页面的主页上的锚标记助手包括一个 asp-route-id
属性, 它表示一个名为 id 的路由值。 路由值作为URL的一部分传递。 如果接收页面定义了匹配的路由参数, 该值将作为 URL 的一部分传递, 例如order/3
。 否则, 它将作为查询字符串值传递:order?id=3
。 无论哪种方式, 传入的值都将绑定到 Id
属性。
接下来,修改 Order.cshtml 的内容如以下所示代码:
@page "{id:int}"@model Bakery.Pages.OrderModel@{ ???ViewData["Title"] = "Place Your Order";}<ol id="orderProcess"> ???<li><span class="step-number">1</span>Choose Item</li> ???<li class="current"><span class="step-number">2</span>Details & Submit</li> ???<li><span class="step-number">3</span>Receipt</li></ol><h1>Place Your Order: @Model.Product.Name</h1>
代码第一行包含 @page
指令, 这是该页面成为 Razor Page 的原因,它还包含以下内容: "{id:int}"。 这是一个路由模板。 这是你在页面定义路由参数的位置。 这个模板定义了一个名为 id
的参数(这将导致主页上的锚标记助手以 id 值作为段生成 url)。 你还添加了一个约束, 在本例中, 你已经指定了 id
的值必须是整数类型(:int)。 基本上,这意味着除非提供 id 路由参数的值,否则无法到达 Order 页面。
现在如果你运行应用程序并点击主页上的的某一个按钮, Order页面将显示所选择产品的名称:
摘要
您已成功使用 BakeryContext 连接到数据库并检索已分配 给PageModel 属性的数据。 它们通过内容页面中的 Model 属性公开,您可以在其中循环显示产品集合以显示它们。 您还在本节中看到了如何在URL中传递数据并利用 BindProperty 属性将路由值映射到 PageModel 中的公共属性。 最后,您已经了解了如何使用该值来查询特定项目,以便您可以显示它的详细信息。
在下一部分中,您将允许用户通过表单提供订单的联系方式和发货详情。
ASP.NET Core Razor Pages 教程五 使用数据
原文地址:https://www.cnblogs.com/mahidol/p/10422884.html