使用ASP.NET Core Razor Pages 构建网站
sqlite 北风数据库
1. Northwind.Common.DataContext.Sqlite
是Sqlite的数据库上下文,有三个类:
ConsoleLogger.cs
NorthwindContext.cs
NorthwindContextExtensions.cs
1.1 NorthwindContext 继承自 Microsoft.EntityFrameworkCore.DbContext
1.1.1 定义了表结构实例
表结构 | 表实例 |
---|---|
类别表 | DbSet<Category> Categories |
消费者表 | DbSet<Customer> Customers |
雇员表 | DbSet<Employee> Employees |
员工地域 | DbSet<EmployeeTerritory> EmployeeTerritories |
订单表 | DbSet<Order> Orders |
订单详情表 | DbSet<OrderDetail> OrderDetails |
产品表 | DbSet<Product> Products |
托运人 | DbSet<Shipper> Shippers |
供应商 | DbSet<Supplier> Suppliers |
地域表 | DbSet<Territory> Territories |
1.1.2模型创建 OnModelCreating()
函数
protected override void OnModelCreating(ModelBuilder modelBuilder)//模型创建
{ //订单详情:多个主键,用FLUENT API 来定义
modelBuilder.Entity<OrderDetail>(entity =>
{ //两个主键
entity.HasKey(e => new { e.OrderId, e.ProductId }); //订单ID、产品ID
//一个订单有多个订单详情
entity.HasOne(d => d.Order)
.WithMany(p => p.OrderDetails)
.HasForeignKey(d => d.OrderId)//外键:订单ID
.OnDelete(DeleteBehavior.ClientSetNull);
//1个产品可以有多个订单详情
entity.HasOne(d => d.Product)
.WithMany(p => p.OrderDetails)
.HasForeignKey(d => d.ProductId)//外键:产品ID
.OnDelete(DeleteBehavior.ClientSetNull);//配置删除主体或切断关系时应用于关系中依赖实体的操作。
});
//产品
modelBuilder.Entity<Product>() //产品单价转为double
.Property(product => product.UnitPrice)
.HasConversion<double>();
OnModelCreatingPartial(modelBuilder);
}
1.2 ConsoleLogger.cs
控制台日志输出功能
1.3 NorthwindContextExtensions.cs
将 NorthwindContext 添加到指定的 IServiceCollection。使用 Sqlite 数据库提供程序。
2. Northwind.Common.EntityModels.Sqlite
2.1 实体的C#类
实体模型表 加粗字段为实体集合需要在类的构造函数中初始化,例如:Customer 类的 Orders
public Customer()
{ //初始化订单
Orders = new HashSet<Order>();
}
粗斜体 为实体对象
实体 | 文件 | 成员 |
---|---|---|
类别 | Category.cs | CategoryId、CategoryName、Description、Picture 、Products |
消费者 | Customer.cs | CustomerId、CompanyName、ContactName、ContactTitle、Address、City、Region、PostalCode、Country、Phone、 Fax、Orders |
雇员 | Employee.cs | EmployeeId、LastName、FirstName、Title、TitleOfCourtesy、BirthDate、HireDate、Address、City、Region、PostalCode、Country、HomePhone、Extension、Photo、Notes、ReportsTo、PhotoPath、Orders |
员工地域 | EmployeeTerritory.cs | EmployeeId、TerritoryId |
订单 | Order.cs | OrderId、CustomerId、EmployeeId、OrderDate、RequiredDate、ShippedDate、ShipVia、Freight、ShipName、ShipAddress、ShipCity、ShipRegion、ShipPostalCode、ShipCountry、Customer、Employee、ShipViaNavigation、OrderDetails |
订单详情 | OrderDetail.cs | OrderId、ProductId、UnitPrice、Quantity、Discount、Order、Product |
产品 | Product.cs | ProductId、ProductName、SupplierId、CategoryId、QuantityPerUnit、UnitPrice、UnitsInStock、UnitsOnOrder、ReorderLevel、Discontinued、Category、Supplier、OrderDetails |
托运人 | Shipper.cs | ShipperId、CompanyName、Phone、Orders |
供应商 | Suppliers.cs | SupplierId、CompanyName、ContactName、ContactTitle、Address、City、Region、PostalCode、Country、Phone、Fax、HomePage、Products |
地域 | Territory.cs | TerritoryId、TerritoryDescription、RegionId |
2.2 特性
[Key]
设置主键
[Required]
非空,必须有
[Column(TypeName = "nvarchar (15)")]
设置与数据库列对应的属性,数据格式
[StringLength(15)]
自定字段允许的最小和最大字符长度
[InverseProperty(nameof(Product.Supplier))]
指定表示同一关系另一端的导航属性的反转
[Index(nameof(CompanyName), Name = "CompanyNameSuppliers")]
指定要在数据库中生成的索引。
[ForeignKey(nameof(CategoryId))]
外键:类别ID
[Table("Order Details")]
指定类映射到的数据库表。
3. Northwind.Razor.Employees 类库
雇员查询网页razor page.
<!--单个雇员_Employee.cshtml-->
@model Packt.Shared.Employee
<div class="card border-dark mb-3" style="max-width: 18rem;">
<div class="card-header">@Model?.LastName, @Model?.FirstName</div>
<div class="card-body text-dark">
<h5 class="card-title">@Model?.Country</h5>
<p class="card-text">@Model?.Notes</p>
</div>
</div>
<!--_ViewStart.cshtml 指定共享布局-->
@{
Layout = "_Layout";
}
<!-- Employees.cshtml 雇员列表-- >
@page
@using Packt.Shared
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@model PacktFeatures.Pages.EmployeesPageModel
<div class="row">
<h1 class="display-2">Employees</h1>
</div>
<div class="row">
@foreach(Employee employee in Model.Employees)
{
<div class="col-sm-3">
<partial name="_Employee" model="employee" />
</div>
}
</div>
//查询雇员
using Microsoft.AspNetCore.Mvc.RazorPages; // PageModel
using Packt.Shared; // Employee, NorthwindContext
namespace PacktFeatures.Pages;
public class EmployeesPageModel : PageModel
{
private NorthwindContext db;
public EmployeesPageModel(NorthwindContext injectedContext)
{
db = injectedContext;
}
public Employee[] Employees { get; set; } = null!;
public void OnGet()
{
ViewData["Title"] = "Northwind B2B - Employees";
Employees = db.Employees.OrderBy(e => e.LastName)
.ThenBy(e => e.FirstName).ToArray();
}
}
4. Northwind.Web
双击项目在编辑器中打开.csproj 文件可看到Web SDK已引用:
<Project Sdk="Microsoft.NET.Sdk.Web">
添加项目引用:
Northwind.Common.DataContext.Sqlite\Northwind.Common.DataContext.Sqlite.csproj
1Northwind.Razor.Employees\Northwind.Razor.Employees.csproj
4.1 Program.cs 有一个Main方法作为入口点
使用默认值配置
Microsoft.Extensions.Hosting.IHostBuilder
以托管 Web 应用程序。这应该在应用程序特定配置之前调用,以避免它覆盖提供的服务、配置源、环境、内容根等。一个 ASP.NET Core 项目就像一个顶级控制台应用程序,以一个隐藏的 Main 方法作为其入口点,该方法有一个使用名称 args 传递的参数。
//对 Run 方法的调用是一个阻塞调用,因此隐藏的 Main 方法在 web 服务器停止运行之前不会返回,如以下代码所示:
Host.CreateDefaultBuilder(args).ConfigureWebHostDetails(webBuilder =>
{
webBuilder.UseStartup<Startup>();//指定 Web 主机要使用的启动类型。
}).Build().Run();
4.2 Startup.cs 进一步配置网页
using Packt.Shared; // AddNorthwindContext extension method
using static System.Console;
namespace Northwind.Web;
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();//将页面的服务添加到指定的 Microsoft.Extensions.DependencyInjection.IServiceCollection。
services.AddNorthwindContext();//将 NorthwindContext 添加到指定的 IServiceCollection。使用 Sqlite 数据库提供程序。
}
public void Configure(
IApplicationBuilder app, IWebHostEnvironment env)
{
if (!env.IsDevelopment())//检查当前主机环境名称是否为 Microsoft.Extensions.Hosting.EnvironmentName.Development。
{
app.UseHsts();//添加使用 HSTS 的中间件,它添加了 Strict-Transport-Security 标头。
}
app.UseRouting(); // 开始端点路由 start endpoint routing
/*将内联定义的中间件委托添加到应用程序的请求管道。
如果您不调用下一个函数,请改用 Microsoft.AspNetCore.Builder.RunExtensions.Run(Microsoft.AspNetCore.Builder.IApplicationBuilder,Microsoft.AspNetCore.Http.RequestDelegate)。
更喜欢使用 Microsoft.AspNetCore.Builder.UseExtensions.Use(Microsoft.AspNetCore.Builder.IApplicationBuilder,System.Func{Microsoft.AspNetCore.Http.HttpContext,Microsoft.AspNetCore.Http.RequestDelegate,System.Threading.Tasks.Task})
// 为了获得更好的性能,如下所示:*/
app.Use(async (HttpContext context, Func<Task> next) =>
{ //表示可用于 URL 匹配或 URL 生成的 Microsoft.AspNetCore.Http.Endpoint。
RouteEndpoint? rep = context.GetEndpoint() as RouteEndpoint;//获取当前请求的 Microsoft.AspNetCore.Http.Endpoint 的扩展方法
if (rep is not null)
{
WriteLine($"Endpoint name: {rep.DisplayName}");//获取此端点的信息显示名称。
WriteLine($"Endpoint route pattern: {rep.RoutePattern.RawText}");//获取解析路由模式时提供的原始文本。可能为空。
}
if (context.Request.Path == "/bonjour")
{
// in the case of a match on URL path, this becomes a terminating
// delegate that returns so does not call the next delegate
// 在 URL 路径匹配的情况下,这将成为返回的终止委托,因此不会调用下一个委托
await context.Response.WriteAsync("Bonjour Monde!");//你好世界
return;
}
//我们可以在调用下一个委托之前修改请求 we could modify the request before calling the next delegate
await next();
//我们可以在调用下一个委托后修改响应 we could modify the response after calling the next delegate
});
app.UseHttpsRedirection();//添加用于将 HTTP 请求重定向到 HTTPS 的中间件。
app.UseDefaultFiles(); //在当前路径上启用默认文件映射 index.html, default.html, and so on
app.UseStaticFiles();//为当前请求路径启用静-态文件服务
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();//将 Razor Pages 的终结点添加到 Microsoft.AspNetCore.Routing.IEndpointRouteBuilder。
//将 Microsoft.AspNetCore.Routing.RouteEndpoint 添加到与指定模式的 HTTP GET 请求匹配的 Microsoft.AspNetCore.Routing.IEndpointRouteBuilder。
endpoints.MapGet("/", () => "Hello World!");//该网站将以纯文本响应所有 HTTP GET 请求:Hello World!。
});
}
}
4.3 共享布局
4.3.1 布局文件:Pages/Shared/_Layout.cshtml
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags必需的元标记 -->
<meta charset="utf-8" />
<meta name="viewport" content=
"width=device-width, initial-scale=1, shrink-to-fit=no" />
<!-- Bootstrap CSS 样式 -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous">
<title>@ViewData["Title"]</title>
</head>
<body>
<div class="container">
@RenderBody()
<hr />
<footer>
<p>Copyright © 2021 - @ViewData["Title"]</p><!-- @ViewData["Title"],使用该布局的页面的变量 -->
</footer>
</div>
<!-- JavaScript to enable features like carousel 启用轮播等功能的 JavaScript -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-U1DAWAznBHeqEIlVSCgzq+c9gqGAJn5c/t99JyeKa9xxaYpSvHU5awsuZVVFIhvj" crossorigin="anonymous"></script>
@RenderSection("Scripts", required: false)<!-- 在布局页,渲染名为 Scripts 的部分 -->
</body>
</html>
4.3.2 _ViewStart.cshtml设置所有 Razor 页面(和所有 MVC 视图)的默认布局文件
@{
Layout = "_Layout";
}
4.4 索引页面
一些按钮,以及链接href
@page
@functions
{
public string? DayName { get; set; } //日期参数
public void OnGet()
{
ViewData["Title"] = "Northwind B2B";
Model.DayName = DateTime.Now.ToString("dddd");
}
}
<div class="jumbotron">
<h1 class="display-3">Welcome to Northwind B2B</h1>
<p class="lead">We supply products to our customers.</p>-
<hr />
<p>It's @Model.DayName! Our customers include restaurants, hotels, and cruise lines.</p>
<p>
<a class="btn btn-primary" href="suppliers">
Learn more about our suppliers
</a>
</p>
<p>
<a class="btn btn-primary" href="packtfeatures/employees">
Contact our employees
</a>
</p>
<p>
<a class="btn btn-primary" href="orders">
How many orders have we taken?
</a>
</p>
<p>
<a class="btn btn-primary" href="customers">
Our customers are global
</a>
</p>
<p>
<a class="btn btn-primary" href="functions">
Play with functions
</a>
</p>
</div>
4.5 订单页面
注入数据库上下文,没有cs文件
@page
@using Packt.Shared
@inject NorthwindContext db
@{
string title = "Orders";
ViewData["Title"] = $"Northwind B2B - {title}";
}
<div class="row">
<h1 class="display-2">@title</h1>
<p>
There are @db.Orders.Count() orders in the Northwind database.
</p>
</div>
4.6 供应商
页面显示供应商表格
@page
@using Packt.Shared
@model Northwind.Web.Pages.SuppliersModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<div class="row">
<h1 class="display-2">Suppliers</h1>
<table class="table">
<thead class="thead-inverse">
<tr>
<th>Company Name</th>
<th>Country</th>
<th>Phone</th>
</tr>
</thead>
<tbody>
@if (Model.Suppliers is not null)
{
@foreach(Supplier s in Model.Suppliers)
{
<tr>
<td>@s.CompanyName</td>
<td>@s.Country</td>
<td>@s.Phone</td>
</tr>
}
}
</tbody>
</table>
</div>
<div class="row">
<p>Enter details for a new supplier:</p>
<form method="POST">
<div><input asp-for="Supplier.CompanyName" placeholder="Company Name" /></div>
<div><input asp-for="Supplier.Country" placeholder="Country" /></div>
<div><input asp-for="Supplier.Phone" placeholder="Phone" /></div>
<input type="submit" />
</form>
</div>
SuppliersModel 继承自PageModel
using Microsoft.AspNetCore.Mvc.RazorPages; // PageModel
using Packt.Shared; // NorthwindContext
using Microsoft.AspNetCore.Mvc; // [BindProperty], IActionResult
namespace Northwind.Web.Pages;
public class SuppliersModel : PageModel
{
public IEnumerable<Supplier>? Suppliers { get; set; }
private NorthwindContext db;
public SuppliersModel(NorthwindContext injectedContext)
{
db = injectedContext;
}
public void OnGet()
{
ViewData["Title"] = "Northwind B2B - Suppliers";
Suppliers = db.Suppliers
.OrderBy(c => c.Country).ThenBy(c => c.CompanyName);
}
[BindProperty]
public Supplier? Supplier { get; set; }// = null!;
public IActionResult OnPost() //提交查询内容
{
if ((Supplier is not null) && ModelState.IsValid)//获取一个值,该值指示此模型状态字典中的任何模型状态值是否无效或未经验证。
{
db.Suppliers.Add(Supplier);//添加供应商
db.SaveChanges();
return RedirectToPage("/suppliers");//重定向到页面,刷新数据
}
else
{
return Page(); // return to original page
}
}
}
4.7 函数计算器页面
@page
@using Northwind.Web.Pages
@using Packt.Shared
@model FunctionsModel
@{
string title = "Functions";
ViewData["Title"] = $"Northwind B2B - {title}";//共享布局参数
string collapsedTimesTable = Model.TimesTableNumberInput.HasValue ? string.Empty : "collapse";
string collapsedCalculateTax = Model.Amount.HasValue ? string.Empty : "collapse";
string collapsedFactorial = Model.FactorialNumber.HasValue ? string.Empty : "collapse";
string collapsedFibonacci = Model.FibonacciNumber.HasValue ? string.Empty : "collapse";
}
<div class="row">
<h1 class="display-2">@title</h1>
<div>
<h2>Exercise 14.3 – Practice building web pages for console apps</h2>
<div>Provide a web user interface to output times tables, calculate tax, and generate factorials and the Fibonacci sequence.</div>
</div>
<div class="accordion" id="accordionFunctions"> <!-- -->
<div class="accordion-item">
<!-- 按钮 -->
<h2 class="accordion-header" id="headerTimesTable">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseTimesTable" aria-expanded="true" aria-controls="collapseTimesTable">
Times Table
</button>
</h2>
<!-- 展开控件 -->
<div id="collapseTimesTable" class="accordion-collapse @collapsedTimesTable" aria-labelledby="headingTimesTable" data-bs-parent="#accordionTimesTable">
<div class="accordion-body">
<form>
<div class="mb-3">
<label for="timesTableNumberInput" class="form-label">Number</label>
<input type="number" class="form-control" id="timesTableNumberInput" name="timesTableNumberInput" aria-describedby="timesTableNumberHelp">
<div id="timesTableNumberHelp" class="form-text">Enter an integer between 1 and 100.</div>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
@if (Model.TimesTableNumberInput.HasValue)
{
<div class="card" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title">@Model.TimesTableNumberInput times table</h5>
@for (int i = 1; i <= 12; i++)
{
<div>
@i x @Model.TimesTableNumberInput = @(i * Model.TimesTableNumberInput)
</div>
}
</div>
</div>
}
</div>
</div>
</div>
<div class="accordion-item">
<!-- 税金计算 手风琴项 -->
<h2 class="accordion-header" id="headerCalculateTax">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseCalculateTax" aria-expanded="true" aria-controls="collapseCalculateTax">
Calculate Tax
</button>
</h2>
<!-- 展开控件 -->
<div id="collapseCalculateTax" class="accordion-collapse @collapsedCalculateTax" aria-labelledby="headingCalculateTax" data-bs-parent="#accordionCalculateTax">
<div class="accordion-body">
<form>
<div class="mb-3">
<label for="calculateTaxAmountInput" class="form-label">Amount</label>
<input type="number" class="form-control" id="calculateTaxAmountInput" name="calculateTaxAmountInput" aria-describedby="calculateTaxAmountInputHelp">
<div id="calculateTaxAmountInputHelp" class="form-text">Enter a monetary value.</div>
</div>
<div class="mb-3">
<label for="calculateTaxRegionCodeInput" class="form-label">Region</label>
<!-- 选择控件:两组选项 -->
<select class="form-control" id="calculateTaxRegionCodeInput" name="calculateTaxRegionCodeInput" aria-describedby="calculateTaxRegionCodeInputHelp">
<optgroup label="Europe">
<option value="DK">Denmark</option>
<option value="FR">France</option>
<option value="HU">Hungary</option>
<option value="NO">Norway</option>
<option value="CH">Switzerland</option>
<option value="GB">United Kingdom</option>
</optgroup>
<optgroup label="United States">
<option value="AK">Alaska</option>
<option value="OR">Oregon</option>
<option value="MT">Montana</option>
<option value="ND">North Dakota</option>
<option value="WI">Wisconsin</option>
<option value="ME">Maine</option>
<option value="VA">Virginia</option>
<option value="CA">California</option>
<option value="OT">Other</option>
</optgroup>
</select>
<div id="calculateTaxRegionCodeInputHelp" class="form-text">Select a European or US state.</div>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
@if (Model.Amount.HasValue)
{
<div class="card" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title">You must pay @Model.TaxToPay in tax.</h5>
</div>
</div>
}
</div>
</div>
</div>
<div class="accordion-item">
<!-- 阶乘计算 -->
<h2 class="accordion-header" id="headerFactorials">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseFactorials" aria-expanded="true" aria-controls="collapseFactorials">
Factorials
</button>
</h2>
<!-- 展开控件 -->
<div id="collapseFactorials" class="accordion-collapse @collapsedFactorial" aria-labelledby="headingFactorials" data-bs-parent="#accordionFactorials">
<div class="accordion-body">
<div>
<form>
<div class="mb-3">
<label for="factorialNumberInput" class="form-label">Number</label>
<input type="number" class="form-control" id="factorialNumberInput" name="factorialNumberInput" aria-describedby="factorialNumberHelp">
<div id="factorialNumberHelp" class="form-text">Enter an integer between 1 and 12.</div>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
@if (Model.FactorialNumber.HasValue)
{
<div class="card" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title">@(Model.FactorialNumber)!</h5>
<div>
@(Model.FactorialNumber)! = @(Model.FactorialResult is null ? "null" : Model.FactorialResult.Value.ToString("N0"))
</div>
</div>
</div>
}
@if (Model.FactorialException is not null)
{
<div class="card" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title">Exception</h5>
<div>
@Model.FactorialException.Message
</div>
</div>
</div>
}
</div>
</div>
</div>
</div>
<div class="accordion-item">
<!-- 斐波那契数列 -->
<h2 class="accordion-header" id="headerFibonacciSequence">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseFibonacciSequence" aria-expanded="true" aria-controls="collapseFibonacciSequence">
Fibonacci sequence
</button>
</h2>
<!-- 展开控件 -->
<div id="collapseFibonacciSequence" class="accordion-collapse @collapsedFibonacci" aria-labelledby="headingFibonacciSequence" data-bs-parent="#accordionCustomers">
<div class="accordion-body">
<div>
<form>
<div class="mb-3">
<label for="fibonacciNumberInput" class="form-label">Term</label>
<input type="number" class="form-control" id="fibonacciNumberInput" name="fibonacciNumberInput" aria-describedby="fibonacciNumberHelp">
<div id="fibonacciNumberHelp" class="form-text">Enter an integer between 1 and 40.</div>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
@if (Model.FibonacciNumber.HasValue)
{
<div class="card" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title">Fibonacci term @Model.FibonacciNumber</h5>
<div>
Term @Model.FibonacciNumber of the fibonacci sequence = @(Model.FibonacciResult is null ? "null" : Model.FibonacciResult.Value.ToString("N0"))
</div>
</div>
</div>
}
</div>
</div>
</div>
</div>
</div>
</div>
<p data-line="631" class="sync-line" style="margin:0;"></p>
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace Northwind.Web.Pages;
public class FunctionsModel : PageModel
{
public int? TimesTableNumberInput { get; set; }
public decimal? Amount { get; set; }
public string? RegionCode { get; set; }
public decimal? TaxToPay { get; set; }
public int? FactorialNumber { get; set; }
public int? FactorialResult { get; set; }
public Exception? FactorialException { get; set; }
public int? FibonacciNumber { get; set; }
public int? FibonacciResult { get; set; }
public void OnGet()//处理请求
{
// Times Table
if (int.TryParse(HttpContext.Request.Query["timesTableNumberInput"], out int i))
{
TimesTableNumberInput = i;//取得乘法表参数
}
// Calculate Tax
if (decimal.TryParse(HttpContext.Request.Query["calculateTaxAmountInput"], out decimal amount))
{
Amount = amount;
RegionCode = HttpContext.Request.Query["calculateTaxRegionCodeInput"];//取得税金计算参数:区域代码
TaxToPay = CalculateTax(amount: amount, twoLetterRegionCode: RegionCode);
}
// Factorial
if (int.TryParse(HttpContext.Request.Query["factorialNumberInput"], out int fact))
{
FactorialNumber = fact;//阶乘计算参数
try
{
FactorialResult = Factorial(fact);
}
catch (Exception ex)
{
FactorialException = ex;
}
}
// Fibonacci
if (int.TryParse(HttpContext.Request.Query["fibonacciNumberInput"], out int fib))
{
FibonacciNumber = fib; //斐波那契数列计算参数
FibonacciResult = FibImperative(term: fib);
}
}
static decimal CalculateTax(//
decimal amount, string twoLetterRegionCode)
{
decimal rate = 0.0M;//税率
// since we are matching string values a switch// 因为我们匹配字符串值一个switch
// 语句比 switch 表达式更简单
// statement is easier than a switch expression
switch (twoLetterRegionCode)
{
case "CH": // Switzerland
rate = 0.08M;
break;
case "DK": // Denmark
case "NO": // Norway
rate = 0.25M;
break;
case "GB": // United Kingdom
case "FR": // France
rate = 0.2M;
break;
case "HU": // Hungary
rate = 0.27M;
break;
case "OR": // Oregon
case "AK": // Alaska
case "MT": // Montana
rate = 0.0M;
break;
case "ND": // North Dakota
case "WI": // Wisconsin
case "ME": // Maine
case "VA": // Virginia
rate = 0.05M;
break;
case "CA": // California
rate = 0.0825M;
break;
default: // most US states
rate = 0.06M;
break;
}
return amount * rate;
}
static int Factorial(int number)//阶乘
{
if (number < 0)
{
throw new ArgumentException(
message: "The factorial function is defined for non-negative integers only.",
paramName: "number");
}
else if (number == 0)
{
return 1;
}
else
{
checked // for overflow
{
return number * Factorial(number - 1);
}
}
}
static int FibImperative(int term)//斐波那契数列
{
if (term == 1)
{
return 0;
}
else if (term == 2)
{
return 1;
}
else
{
return FibImperative(term - 1) + FibImperative(term - 2);
}
}
}
4.8 消费者页面
@page
@using Northwind.Web.Pages
@using Packt.Shared
@model CustomersModel
@{
string title = "Customers by Country";
ViewData["Title"] = $"Northwind B2B - {title}";//浏览器选项卡页标题
}
<div class="row">
<h1 class="display-2">@title</h1>
<div>
<h2>Exercise 14.2 – Practice building a data-driven web page</h2>
</div>
<div class="accordion" id="accordionCustomers"><!--accordion手风琴-->
@if (Model.CustomersByCountry is not null)
{
@foreach (IGrouping<string?, Customer> cbc in Model.CustomersByCountry)
{
<div class="accordion-item"> <!-- 手风琴项-->
<h2 class="accordion-header" id="header@(cbc.Key)">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapse@(cbc.Key)" aria-expanded="true" aria-controls="collapse@(cbc.Key)">
@cbc.Key has @cbc.Count() customers
</button>
</h2>
<div id="collapse@(cbc.Key)" class="accordion-collapse collapse" aria-labelledby="heading@(cbc.Key)" data-bs-parent="#accordionCustomers">
<div class="accordion-body">
<ul> <!-- 列出消费者-->
@foreach (Customer c in cbc)
{
<li><a href="customerorders?id=@c.CustomerId"> <!-- 链接到订单页面 customerorders id 作为请求信息 被 订单页面检索-->
@c.CompanyName</a></li>
}
</ul>
</div>
</div>
</div>
}
}
</div>
</div>
using Microsoft.AspNetCore.Mvc.RazorPages; // PageModel
using Packt.Shared; // Customer
namespace Northwind.Web.Pages;
public class CustomersModel : PageModel
{//为将键映射到 System.Collections.Generic.IEnumerable`1 值序列的数据结构定义索引器、大小属性和布尔搜索方法。
public ILookup<string?, Customer>? CustomersByCountry;
private NorthwindContext db;
public CustomersModel(NorthwindContext db)
{
this.db = db;//初始化数据库
}
public void OnGet()
{ //根据指定的键选择器函数从 System.Collections.Generic.IEnumerable`1 创建 System.Linq.Lookup`2。
//返回:包含键和值的 System.Linq.Lookup`2。每个组中的值与源中的顺序相同。
CustomersByCountry = db.Customers.ToLookup(c => c.Country);
}
}
4.9 CustomerOrders消费者的订单页面
@page
@using Northwind.Web.Pages
@using Packt.Shared
@model CustomerOrdersModel
@{
string title = "Customer and their orders";
ViewData["Title"] = $"Northwind B2B - {title}";
}
<div class="row">
<h1 class="display-2">@title</h1>
<div>
@if (Model.Customer is not null)
{
<div>
<div>@Model.Customer.CompanyName</div>
</div>
<div>
<table> <!-- 表-->
<thead> <!-- 表头-->
<tr><th>Order Id</th><th>Order Date</th></tr>
</thead>
<tbody> <!-- 表数据 -->
@foreach (Order o in Model.Customer.Orders)
{ <!-- td -->
<tr><td>@o.OrderId</td><td>@o.OrderDate</td></tr>
}
</tbody>
</table>
</div>
}
</div>
</div>
using Microsoft.AspNetCore.Mvc.RazorPages; // PageModel
using Microsoft.EntityFrameworkCore; // Include extension method
using Packt.Shared; // Customer
namespace Northwind.Web.Pages;
public class CustomerOrdersModel : PageModel
{
public Customer? Customer;
private NorthwindContext db;
public CustomerOrdersModel(NorthwindContext db)
{
this.db = db;
}
public void OnGet()
{
string id = HttpContext.Request.Query["id"];//获取http请求中的 "id"
Customer = db.Customers.Include(c => c.Orders)
.SingleOrDefault(c => c.CustomerId == id);
}
}
The End