mirror of
https://github.com/markjprice/cs11dotnet7.git
synced 2026-01-26 09:44:15 +01:00
Add improvement item for page 369
This commit is contained in:
parent
f827a14e78
commit
5ebd5162a4
|
|
@ -4,7 +4,7 @@ If you find any mistakes in the seventh edition, *C# 11 and .NET 7 - Modern Cros
|
|||
|
||||
[**Errata** (47 items)](errata.md): Typos, tool user interface changes, or mistakes in code that would cause a compilation error that prevents a successful build.
|
||||
|
||||
[**Improvements** (42 items)](improvements.md): Changes to text or code that would improve the content. These are optional.
|
||||
[**Improvements** (43 items)](improvements.md): Changes to text or code that would improve the content. These are optional.
|
||||
|
||||
[**Common Errors** (6 items)](common-errors.md): These are some of the most common errors that a reader might encounter when trying to get code in book tasks to work, or when trying to write your own code.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
**Improvements** (42 items)
|
||||
**Improvements** (43 items)
|
||||
|
||||
If you have suggestions for improvements, then please [raise an issue in this repository](https://github.com/markjprice/cs11dotnet7/issues) or email me at markjprice (at) gmail.com.
|
||||
|
||||
|
|
@ -28,6 +28,7 @@ If you have suggestions for improvements, then please [raise an issue in this re
|
|||
- [Page 339 - Viewing source links with Visual Studio 2022](#page-339---viewing-source-links-with-visual-studio-2022)
|
||||
- [Page 343 - Packaging a library for NuGet](#page-343---packaging-a-library-for-nuget)
|
||||
- [Page 351 - Using non-.NET Standard libraries](#page-351---using-non-net-standard-libraries)
|
||||
- [Page 369 - Activating regular expression syntax coloring](#page-369---activating-regular-expression-syntax-coloring)
|
||||
- [Page 378 - Dictionaries](#page-378---dictionaries)
|
||||
- [Page 444 - Connecting to a database](#page-444---connecting-to-a-database)
|
||||
- [Page 453 - Scaffolding models using an existing database](#page-453---scaffolding-models-using-an-existing-database)
|
||||
|
|
@ -706,6 +707,17 @@ for (int i = 0; i < matrix.Axes[1].Points.Length; i++)
|
|||
}
|
||||
```
|
||||
|
||||
# Page 369 - Activating regular expression syntax coloring
|
||||
|
||||
In Step 2, I wrote, "Right-click in the `StringSyntax` attribute, select **Go To Implementation**, and note..."
|
||||
|
||||
Visual Studio 2022 has two similar features:
|
||||
|
||||
- **Go To Definition** *F12*: Should go to the decompiled metadata for a member or type. But if you have previously viewed source link, then it goes to source link!
|
||||
- **Go To Implementation** *Ctrl* + *F12*: Should go to the source link implementation for a member or type. But if you have disabled source link, then it goes to the decompiled metadata.
|
||||
|
||||
In the next edition, I will add a note about this.
|
||||
|
||||
# Page 378 - Dictionaries
|
||||
|
||||
In the next edition, I will add a note at the bottom of this section to set an expectation that readers will come across dictionaries again later in the book in more practical ways.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
@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)
|
||||
{
|
||||
<tr><td>@o.OrderId</td><td>@o.OrderDate</td></tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
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"];
|
||||
|
||||
Customer = db.Customers.Include(c => c.Orders)
|
||||
.SingleOrDefault(c => c.CustomerId == id);
|
||||
}
|
||||
}
|
||||
40
vs4win/PracticalApps/Northwind.Web/Pages/Customers.cshtml
Normal file
40
vs4win/PracticalApps/Northwind.Web/Pages/Customers.cshtml
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
@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">
|
||||
@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">
|
||||
@c.CompanyName</a></li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
21
vs4win/PracticalApps/Northwind.Web/Pages/Customers.cshtml.cs
Normal file
21
vs4win/PracticalApps/Northwind.Web/Pages/Customers.cshtml.cs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
using Microsoft.AspNetCore.Mvc.RazorPages; // PageModel
|
||||
using Packt.Shared; // Customer
|
||||
|
||||
namespace Northwind.Web.Pages;
|
||||
|
||||
public class CustomersModel : PageModel
|
||||
{
|
||||
public ILookup<string?, Customer>? CustomersByCountry;
|
||||
|
||||
private NorthwindContext db;
|
||||
|
||||
public CustomersModel(NorthwindContext db)
|
||||
{
|
||||
this.db = db;
|
||||
}
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
CustomersByCountry = db.Customers.ToLookup(c => c.Country);
|
||||
}
|
||||
}
|
||||
190
vs4win/PracticalApps/Northwind.Web/Pages/Functions.cshtml
Normal file
190
vs4win/PracticalApps/Northwind.Web/Pages/Functions.cshtml
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
@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>
|
||||
141
vs4win/PracticalApps/Northwind.Web/Pages/Functions.cshtml.cs
Normal file
141
vs4win/PracticalApps/Northwind.Web/Pages/Functions.cshtml.cs
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
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
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
@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)
|
||||
{
|
||||
<tr><td>@o.OrderId</td><td>@o.OrderDate</td></tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
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"];
|
||||
|
||||
Customer = db.Customers.Include(c => c.Orders)
|
||||
.SingleOrDefault(c => c.CustomerId == id);
|
||||
}
|
||||
}
|
||||
40
vscode/PracticalApps/Northwind.Web/Pages/Customers.cshtml
Normal file
40
vscode/PracticalApps/Northwind.Web/Pages/Customers.cshtml
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
@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">
|
||||
@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">
|
||||
@c.CompanyName</a></li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
21
vscode/PracticalApps/Northwind.Web/Pages/Customers.cshtml.cs
Normal file
21
vscode/PracticalApps/Northwind.Web/Pages/Customers.cshtml.cs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
using Microsoft.AspNetCore.Mvc.RazorPages; // PageModel
|
||||
using Packt.Shared; // Customer
|
||||
|
||||
namespace Northwind.Web.Pages;
|
||||
|
||||
public class CustomersModel : PageModel
|
||||
{
|
||||
public ILookup<string?, Customer>? CustomersByCountry;
|
||||
|
||||
private NorthwindContext db;
|
||||
|
||||
public CustomersModel(NorthwindContext db)
|
||||
{
|
||||
this.db = db;
|
||||
}
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
CustomersByCountry = db.Customers.ToLookup(c => c.Country);
|
||||
}
|
||||
}
|
||||
190
vscode/PracticalApps/Northwind.Web/Pages/Functions.cshtml
Normal file
190
vscode/PracticalApps/Northwind.Web/Pages/Functions.cshtml
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
@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>
|
||||
141
vscode/PracticalApps/Northwind.Web/Pages/Functions.cshtml.cs
Normal file
141
vscode/PracticalApps/Northwind.Web/Pages/Functions.cshtml.cs
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
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
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in a new issue