Add improvement item for page 369

This commit is contained in:
Mark J Price 2023-07-26 17:45:45 +01:00
parent f827a14e78
commit 5ebd5162a4
14 changed files with 912 additions and 2 deletions

View file

@ -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.

View file

@ -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.

View file

@ -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>

View file

@ -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);
}
}

View 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>

View 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);
}
}

View 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>

View 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);
}
}
}

View file

@ -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>

View file

@ -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);
}
}

View 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>

View 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);
}
}

View 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>

View 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);
}
}
}