Část 3, Razor Stránky s EF Core ASP.NET jádrem – řazení, filtrování, stránkování
Tom Dykstra, Jeremy Likness a Jon P Smith
Webová aplikace Contoso University ukazuje, jak vytvářet Razor webové aplikace Pages pomocí EF Core sady Visual Studio. Informace o sérii kurzů najdete v prvním kurzu.
Pokud narazíte na problémy, které nemůžete vyřešit, stáhněte si dokončenou aplikaci a porovnejte tento kód s tím, co jste vytvořili podle kurzu.
V tomto kurzu přidáte funkce řazení, filtrování a stránkování na stránky Studenti.
Následující obrázek znázorňuje dokončenou stránku. Záhlaví sloupců se dají seřadit kliknutím na odkazy. Opakovaným kliknutím na záhlaví sloupce můžete přepínat mezi vzestupným a sestupným pořadím řazení.
Přidání řazení
Nahraďte kód Pages/Students/Index.cshtml.cs
následujícím kódem pro přidání řazení.
public class IndexModel : PageModel
{
private readonly SchoolContext _context;
public IndexModel(SchoolContext context)
{
_context = context;
}
public string NameSort { get; set; }
public string DateSort { get; set; }
public string CurrentFilter { get; set; }
public string CurrentSort { get; set; }
public IList<Student> Students { get; set; }
public async Task OnGetAsync(string sortOrder)
{
// using System;
NameSort = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
DateSort = sortOrder == "Date" ? "date_desc" : "Date";
IQueryable<Student> studentsIQ = from s in _context.Students
select s;
switch (sortOrder)
{
case "name_desc":
studentsIQ = studentsIQ.OrderByDescending(s => s.LastName);
break;
case "Date":
studentsIQ = studentsIQ.OrderBy(s => s.EnrollmentDate);
break;
case "date_desc":
studentsIQ = studentsIQ.OrderByDescending(s => s.EnrollmentDate);
break;
default:
studentsIQ = studentsIQ.OrderBy(s => s.LastName);
break;
}
Students = await studentsIQ.AsNoTracking().ToListAsync();
}
}
Předchozí kód:
- Vyžaduje přidání
using System;
. - Přidá vlastnosti, které budou obsahovat parametry řazení.
- Změní název
Student
vlastnosti naStudents
. - Nahradí kód v
OnGetAsync
metodě.
Metoda OnGetAsync
obdrží sortOrder
parametr z řetězce dotazu v adrese URL. Adresa URL a řetězec dotazu je generována pomocným rutinou značky ukotvení.
Parametr sortOrder
je buď Name
nebo Date
. Za sortOrder
parametrem _desc
můžete volitelně zadat sestupné pořadí. Výchozí pořadí řazení je vzestupné.
Když je stránka Index požadována z odkazu Studenti , neexistuje žádný řetězec dotazu. Studenti se zobrazují vzestupně podle příjmení. Vzestupné pořadí podle příjmení je default
v switch
příkazu. Když uživatel klikne na odkaz záhlaví sloupce, zobrazí se v řetězcové hodnotě dotazu příslušná sortOrder
hodnota.
NameSort
a DateSort
stránka slouží Razor ke konfiguraci hypertextových odkazů záhlaví sloupců s příslušnými hodnotami řetězce dotazu:
NameSort = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
DateSort = sortOrder == "Date" ? "date_desc" : "Date";
Kód používá podmíněný operátor jazyka C# :. Operátor ?:
je ternární operátor, má tři operandy. První řádek určuje, že pokud sortOrder
je hodnota null nebo prázdná, NameSort
je nastavena na name_desc
hodnotu . Pokud sortOrder
hodnota null nebo není prázdná, NameSort
nastaví se na prázdný řetězec.
Tyto dva příkazy umožňují stránce nastavit hypertextové odkazy záhlaví sloupců následujícím způsobem:
Aktuální pořadí řazení | Hypertextový odkaz na příjmení | Hypertextový odkaz na datum |
---|---|---|
Příjmení vzestupně | descending | ascending |
Příjmení sestupně | ascending | ascending |
Vzestupné datum | ascending | descending |
Datum sestupně | ascending | ascending |
Metoda používá LINQ to Entities k určení sloupce, podle který se má seřadit. Kód inicializuje IQueryable<Student>
před příkaz switch a upraví ho v příkazu switch:
IQueryable<Student> studentsIQ = from s in _context.Students
select s;
switch (sortOrder)
{
case "name_desc":
studentsIQ = studentsIQ.OrderByDescending(s => s.LastName);
break;
case "Date":
studentsIQ = studentsIQ.OrderBy(s => s.EnrollmentDate);
break;
case "date_desc":
studentsIQ = studentsIQ.OrderByDescending(s => s.EnrollmentDate);
break;
default:
studentsIQ = studentsIQ.OrderBy(s => s.LastName);
break;
}
Students = await studentsIQ.AsNoTracking().ToListAsync();
IQueryable
Při vytvoření nebo úpravě se do databáze neposílají žádné dotazy. Dotaz se nespustí, dokud IQueryable
se objekt nepřevedí do kolekce. IQueryable
jsou převedeny na kolekci voláním metody, například ToListAsync
. IQueryable
Výsledkem kódu je tedy jeden dotaz, který se nespustí, dokud nebude následující příkaz:
Students = await studentsIQ.AsNoTracking().ToListAsync();
OnGetAsync
mohlo by se dostat do podrobného formátu s velkým počtem seřaditelných sloupců. Informace o alternativním způsobu kódování této funkce najdete v tématu Použití dynamického LINQ ke zjednodušení kódu ve verzi MVC této série kurzů.
Přidání hypertextových odkazů záhlaví sloupců na stránku Index studenta
Nahraďte kód v Students/Index.cshtml
následujícím kódu. Změny jsou zvýrazněné.
@page
@model ContosoUniversity.Pages.Students.IndexModel
@{
ViewData["Title"] = "Students";
}
<h2>Students</h2>
<p>
<a asp-page="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
<a asp-page="./Index" asp-route-sortOrder="@Model.NameSort">
@Html.DisplayNameFor(model => model.Students[0].LastName)
</a>
</th>
<th>
@Html.DisplayNameFor(model => model.Students[0].FirstMidName)
</th>
<th>
<a asp-page="./Index" asp-route-sortOrder="@Model.DateSort">
@Html.DisplayNameFor(model => model.Students[0].EnrollmentDate)
</a>
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model.Students)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
@Html.DisplayFor(modelItem => item.FirstMidName)
</td>
<td>
@Html.DisplayFor(modelItem => item.EnrollmentDate)
</td>
<td>
<a asp-page="./Edit" asp-route-id="@item.ID">Edit</a> |
<a asp-page="./Details" asp-route-id="@item.ID">Details</a> |
<a asp-page="./Delete" asp-route-id="@item.ID">Delete</a>
</td>
</tr>
}
</tbody>
</table>
Předchozí kód:
- Přidá hypertextové odkazy do
LastName
záhlaví sloupců aEnrollmentDate
sloupců. - Používá informace v
NameSort
aDateSort
k nastavení hypertextových odkazů s aktuálními hodnotami pořadí řazení. - Změní nadpis stránky z indexu na Studenty.
- Změny
Model.Student
v souboruModel.Students
.
Ověření, že řazení funguje:
- Spusťte aplikaci a vyberte kartu Studenti .
- Klikněte na záhlaví sloupců.
Přidání filtrování
Přidání filtrování na stránku Index studentů:
- Na stránku se přidá Razor textové pole a tlačítko odeslat. Textové pole poskytuje hledaný řetězec na křestní jméno nebo příjmení.
- Model stránky se aktualizuje tak, aby používal hodnotu textového pole.
Aktualizace metody OnGetAsync
Nahraďte kód Students/Index.cshtml.cs
následujícím kódem a přidejte filtrování:
public class IndexModel : PageModel
{
private readonly SchoolContext _context;
public IndexModel(SchoolContext context)
{
_context = context;
}
public string NameSort { get; set; }
public string DateSort { get; set; }
public string CurrentFilter { get; set; }
public string CurrentSort { get; set; }
public IList<Student> Students { get; set; }
public async Task OnGetAsync(string sortOrder, string searchString)
{
NameSort = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
DateSort = sortOrder == "Date" ? "date_desc" : "Date";
CurrentFilter = searchString;
IQueryable<Student> studentsIQ = from s in _context.Students
select s;
if (!String.IsNullOrEmpty(searchString))
{
studentsIQ = studentsIQ.Where(s => s.LastName.Contains(searchString)
|| s.FirstMidName.Contains(searchString));
}
switch (sortOrder)
{
case "name_desc":
studentsIQ = studentsIQ.OrderByDescending(s => s.LastName);
break;
case "Date":
studentsIQ = studentsIQ.OrderBy(s => s.EnrollmentDate);
break;
case "date_desc":
studentsIQ = studentsIQ.OrderByDescending(s => s.EnrollmentDate);
break;
default:
studentsIQ = studentsIQ.OrderBy(s => s.LastName);
break;
}
Students = await studentsIQ.AsNoTracking().ToListAsync();
}
}
Předchozí kód:
searchString
Přidá parametr doOnGetAsync
metody a uloží hodnotu parametruCurrentFilter
do vlastnosti. Hodnota vyhledávacího řetězce se přijímá z textového pole přidaného v další části.- Přidá do příkazu LINQ klauzuli
Where
. KlauzuleWhere
vybere jenom studenty, jejichž křestní jméno nebo příjmení obsahuje hledaný řetězec. Příkaz LINQ se spustí pouze v případě, že je k dispozici hodnota, kterou chcete vyhledat.
IQueryable vs. IEnumerable
Kód volá metodu Where objektu IQueryable
a filtr je zpracován na serveru. V některých scénářích může aplikace volat metodu Where
jako metodu rozšíření v kolekci v paměti. Předpokládejme například, že _context.Students
se změny z EF CoreDbSet
metody úložiště, která vrací kolekci IEnumerable
. Výsledek by obvykle byl stejný, ale v některých případech se může lišit.
Například implementace Contains
rozhraní .NET Framework ve výchozím nastavení provádí porovnání s rozlišováním velkých a malých písmen. V SYSTÉMU SQL Server Contains
je citlivost na velká a malá písmena určena nastavením kolace instance SYSTÉMU SQL Server. SQL Server ve výchozím nastavení nerozlišuje malá a velká písmena. Funkce SQLite ve výchozím nastavení rozlišují malá a velká písmena. ToUpper
lze volat, aby test explicitně nerozlišil malá a velká písmena:
Where(s => s.LastName.ToUpper().Contains(searchString.ToUpper())`
Předchozí kód zajistí, že filtr nerozlišuje malá a velká písmena, i když Where
je metoda volána na IEnumerable
rozhraní SQLite nebo se spustí na rozhraní SQLite.
Při Contains
zavolání kolekce IEnumerable
se použije implementace .NET Core. Při Contains
zavolání objektu IQueryable
se použije implementace databáze.
Volání Contains
na službu IQueryable
je obvykle vhodnější z důvodů výkonu. Při IQueryable
použití se filtrování provádí databázovým serverem. IEnumerable
Pokud se vytvoří nejprve, musí se všechny řádky vrátit z databázového serveru.
Je tu trest za výkon za volání ToUpper
. Kód ToUpper
přidá funkci v klauzuli WHERE příkazu TSQL SELECT. Přidaná funkce brání optimalizátoru v používání indexu. Vzhledem k tomu, že sql je nainstalovaný jako nerozlišující velká a malá písmena, je nejlepší se vyhnout ToUpper
volání, když není potřeba.
Další informace naleznete v tématu Použití dotazů nerozlišující velká a malá písmena u zprostředkovatele Sqlite.
Razor Aktualizace stránky
Nahraďte kód Pages/Students/Index.cshtml
pro přidání tlačítka Hledat .
@page
@model ContosoUniversity.Pages.Students.IndexModel
@{
ViewData["Title"] = "Students";
}
<h2>Students</h2>
<p>
<a asp-page="Create">Create New</a>
</p>
<form asp-page="./Index" method="get">
<div class="form-actions no-color">
<p>
Find by name:
<input type="text" name="SearchString" value="@Model.CurrentFilter" />
<input type="submit" value="Search" class="btn btn-primary" /> |
<a asp-page="./Index">Back to full List</a>
</p>
</div>
</form>
<table class="table">
<thead>
<tr>
<th>
<a asp-page="./Index" asp-route-sortOrder="@Model.NameSort">
@Html.DisplayNameFor(model => model.Students[0].LastName)
</a>
</th>
<th>
@Html.DisplayNameFor(model => model.Students[0].FirstMidName)
</th>
<th>
<a asp-page="./Index" asp-route-sortOrder="@Model.DateSort">
@Html.DisplayNameFor(model => model.Students[0].EnrollmentDate)
</a>
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model.Students)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
@Html.DisplayFor(modelItem => item.FirstMidName)
</td>
<td>
@Html.DisplayFor(modelItem => item.EnrollmentDate)
</td>
<td>
<a asp-page="./Edit" asp-route-id="@item.ID">Edit</a> |
<a asp-page="./Details" asp-route-id="@item.ID">Details</a> |
<a asp-page="./Delete" asp-route-id="@item.ID">Delete</a>
</td>
</tr>
}
</tbody>
</table>
Předchozí kód pomocí pomocné rutiny <form>
značky přidá vyhledávací textové pole a tlačítko. Ve výchozím nastavení <form>
pomocník značky odesílá data formuláře pomocí post. U post se parametry předávají v textu zprávy HTTP, a ne v adrese URL. Při použití http GET se data formuláře předávají v adrese URL jako řetězce dotazu. Předání dat pomocí řetězců dotazu umožňuje uživatelům vytvořit záložku adresy URL. Pokyny W3C doporučují, aby se funkce GET používala, když akce nemá za následek aktualizaci.
Otestujte aplikaci:
Vyberte kartu Studenti a zadejte hledaný řetězec. Pokud používáte SQLite, filtr nerozlišuje malá a velká písmena jenom v případě, že jste implementovali volitelný
ToUpper
kód zobrazený dříve.Vyberte Hledat.
Všimněte si, že adresa URL obsahuje hledaný řetězec. Příklad:
https://localhost:5001/Students?SearchString=an
Pokud je stránka záložka, záložka obsahuje adresu URL stránky a SearchString
řetězec dotazu. V method="get"
značce form
je to, co způsobilo vygenerování řetězce dotazu.
Když je v současné době vybrán odkaz pro řazení záhlaví sloupce, hodnota filtru z vyhledávacího pole se ztratí. Ztracená hodnota filtru je opravena v další části.
Přidání stránkování
V této části se vytvoří třída, PaginatedList
která podporuje stránkování. Třída PaginatedList
používá Skip
a Take
příkazy k filtrování dat na serveru místo načtení všech řádků tabulky. Následující obrázek znázorňuje stránkovací tlačítka.
Vytvoření třídy PaginatedList
Ve složce projektu vytvořte PaginatedList.cs
následující kód:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
namespace ContosoUniversity
{
public class PaginatedList<T> : List<T>
{
public int PageIndex { get; private set; }
public int TotalPages { get; private set; }
public PaginatedList(List<T> items, int count, int pageIndex, int pageSize)
{
PageIndex = pageIndex;
TotalPages = (int)Math.Ceiling(count / (double)pageSize);
this.AddRange(items);
}
public bool HasPreviousPage => PageIndex > 1;
public bool HasNextPage => PageIndex < TotalPages;
public static async Task<PaginatedList<T>> CreateAsync(
IQueryable<T> source, int pageIndex, int pageSize)
{
var count = await source.CountAsync();
var items = await source.Skip(
(pageIndex - 1) * pageSize)
.Take(pageSize).ToListAsync();
return new PaginatedList<T>(items, count, pageIndex, pageSize);
}
}
}
Metoda CreateAsync
v předchozím kódu má velikost stránky a číslo stránky a použije příslušné Skip
příkazy a Take
příkazy na IQueryable
. Při ToListAsync
zavolání na této IQueryable
stránce vrátí seznam obsahující pouze požadovanou stránku. Vlastnosti HasPreviousPage
a HasNextPage
slouží k povolení nebo zakázání předchozí a další stránkovací tlačítka.
Metoda CreateAsync
se používá k vytvoření PaginatedList<T>
. Konstruktor nemůže vytvořit PaginatedList<T>
objekt. Konstruktory nemůžou spouštět asynchronní kód.
Přidání velikosti stránky do konfigurace
Přidejte PageSize
do konfiguračního appsettings.json
souboru:
{
"PageSize": 3,
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"SchoolContext": "Server=(localdb)\\mssqllocaldb;Database=CU-1;Trusted_Connection=True;MultipleActiveResultSets=true"
}
}
Přidání stránkování do modelu IndexModel
Nahraďte kód Students/Index.cshtml.cs
pro přidání stránkování.
using ContosoUniversity.Data;
using ContosoUniversity.Models;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using System;
using System.Linq;
using System.Threading.Tasks;
namespace ContosoUniversity.Pages.Students
{
public class IndexModel : PageModel
{
private readonly SchoolContext _context;
private readonly IConfiguration Configuration;
public IndexModel(SchoolContext context, IConfiguration configuration)
{
_context = context;
Configuration = configuration;
}
public string NameSort { get; set; }
public string DateSort { get; set; }
public string CurrentFilter { get; set; }
public string CurrentSort { get; set; }
public PaginatedList<Student> Students { get; set; }
public async Task OnGetAsync(string sortOrder,
string currentFilter, string searchString, int? pageIndex)
{
CurrentSort = sortOrder;
NameSort = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
DateSort = sortOrder == "Date" ? "date_desc" : "Date";
if (searchString != null)
{
pageIndex = 1;
}
else
{
searchString = currentFilter;
}
CurrentFilter = searchString;
IQueryable<Student> studentsIQ = from s in _context.Students
select s;
if (!String.IsNullOrEmpty(searchString))
{
studentsIQ = studentsIQ.Where(s => s.LastName.Contains(searchString)
|| s.FirstMidName.Contains(searchString));
}
switch (sortOrder)
{
case "name_desc":
studentsIQ = studentsIQ.OrderByDescending(s => s.LastName);
break;
case "Date":
studentsIQ = studentsIQ.OrderBy(s => s.EnrollmentDate);
break;
case "date_desc":
studentsIQ = studentsIQ.OrderByDescending(s => s.EnrollmentDate);
break;
default:
studentsIQ = studentsIQ.OrderBy(s => s.LastName);
break;
}
var pageSize = Configuration.GetValue("PageSize", 4);
Students = await PaginatedList<Student>.CreateAsync(
studentsIQ.AsNoTracking(), pageIndex ?? 1, pageSize);
}
}
}
Předchozí kód:
- Změní typ
Students
vlastnosti zIList<Student>
naPaginatedList<Student>
. - Přidá index stránky, aktuální
sortOrder
acurrentFilter
doOnGetAsync
podpisu metody. - Uloží pořadí řazení ve
CurrentSort
vlastnosti. - Obnoví index stránky na hodnotu 1, pokud je nový hledaný řetězec.
PaginatedList
Používá třídu k získání entit studenta.- Nastaví
pageSize
3 z konfigurace, 4, pokud se konfigurace nezdaří.
Všechny parametry, které OnGetAsync
obdrží, mají hodnotu null v následujících případech:
- Stránka se volá z odkazu Studenti .
- Uživatel neklikli na stránkovací nebo seřazovací odkaz.
Po kliknutí na stránkovací odkaz obsahuje proměnná indexu stránky číslo stránky, které se má zobrazit.
Vlastnost CurrentSort
poskytuje Razor Page s aktuálním pořadím řazení. Aktuální pořadí řazení musí být zahrnuto ve stránkovacích odkazech, aby bylo možné zachovat pořadí řazení při stránkování.
Vlastnost CurrentFilter
poskytuje Razor Page s aktuálním řetězcem filtru. Hodnota CurrentFilter
:
- Aby bylo možné zachovat nastavení filtru během stránkování, musí být součástí stránkovacího odkazu.
- Po opětovném zobrazení stránky je nutné obnovit textové pole do textového pole.
Pokud se při stránkování změní hledaný řetězec, stránka se resetuje na 1. Stránka se musí resetovat na 1, protože nový filtr může vést k zobrazení různých dat. Když je zadána hodnota hledání a je vybrána možnost Odeslat :
- Vyhledávací řetězec se změní.
- Parametr
searchString
nemá hodnotu null.
Metoda PaginatedList.CreateAsync
převede dotaz studenta na jednu stránku studentů v typu kolekce, která podporuje stránkování. Tato jedna stránka studentů se předá na Razor stránku.
Dvě otazníky za pageIndex
PaginatedList.CreateAsync
voláním představují operátor nulového sjednocení. Operátor nulového sjednocení definuje výchozí hodnotu pro typ s možnou hodnotou null. Výraz pageIndex ?? 1
vrátí hodnotu pageIndex
, pokud má hodnotu, jinak vrátí hodnotu 1.
Přidání stránkovacího odkazu
Nahraďte kód v Students/Index.cshtml
následujícím kódem. Změny jsou zvýrazněné:
@page
@model ContosoUniversity.Pages.Students.IndexModel
@{
ViewData["Title"] = "Students";
}
<h2>Students</h2>
<p>
<a asp-page="Create">Create New</a>
</p>
<form asp-page="./Index" method="get">
<div class="form-actions no-color">
<p>
Find by name:
<input type="text" name="SearchString" value="@Model.CurrentFilter" />
<input type="submit" value="Search" class="btn btn-primary" /> |
<a asp-page="./Index">Back to full List</a>
</p>
</div>
</form>
<table class="table">
<thead>
<tr>
<th>
<a asp-page="./Index" asp-route-sortOrder="@Model.NameSort"
asp-route-currentFilter="@Model.CurrentFilter">
@Html.DisplayNameFor(model => model.Students[0].LastName)
</a>
</th>
<th>
@Html.DisplayNameFor(model => model.Students[0].FirstMidName)
</th>
<th>
<a asp-page="./Index" asp-route-sortOrder="@Model.DateSort"
asp-route-currentFilter="@Model.CurrentFilter">
@Html.DisplayNameFor(model => model.Students[0].EnrollmentDate)
</a>
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model.Students)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
@Html.DisplayFor(modelItem => item.FirstMidName)
</td>
<td>
@Html.DisplayFor(modelItem => item.EnrollmentDate)
</td>
<td>
<a asp-page="./Edit" asp-route-id="@item.ID">Edit</a> |
<a asp-page="./Details" asp-route-id="@item.ID">Details</a> |
<a asp-page="./Delete" asp-route-id="@item.ID">Delete</a>
</td>
</tr>
}
</tbody>
</table>
@{
var prevDisabled = !Model.Students.HasPreviousPage ? "disabled" : "";
var nextDisabled = !Model.Students.HasNextPage ? "disabled" : "";
}
<a asp-page="./Index"
asp-route-sortOrder="@Model.CurrentSort"
asp-route-pageIndex="@(Model.Students.PageIndex - 1)"
asp-route-currentFilter="@Model.CurrentFilter"
class="btn btn-primary @prevDisabled">
Previous
</a>
<a asp-page="./Index"
asp-route-sortOrder="@Model.CurrentSort"
asp-route-pageIndex="@(Model.Students.PageIndex + 1)"
asp-route-currentFilter="@Model.CurrentFilter"
class="btn btn-primary @nextDisabled">
Next
</a>
Odkazy záhlaví sloupce používají řetězec dotazu k předání aktuálního vyhledávacího řetězce metodě OnGetAsync
:
<a asp-page="./Index" asp-route-sortOrder="@Model.NameSort"
asp-route-currentFilter="@Model.CurrentFilter">
@Html.DisplayNameFor(model => model.Students[0].LastName)
</a>
Stránkovací tlačítka se zobrazují pomocnými rutiny značek:
<a asp-page="./Index"
asp-route-sortOrder="@Model.CurrentSort"
asp-route-pageIndex="@(Model.Students.PageIndex - 1)"
asp-route-currentFilter="@Model.CurrentFilter"
class="btn btn-primary @prevDisabled">
Previous
</a>
<a asp-page="./Index"
asp-route-sortOrder="@Model.CurrentSort"
asp-route-pageIndex="@(Model.Students.PageIndex + 1)"
asp-route-currentFilter="@Model.CurrentFilter"
class="btn btn-primary @nextDisabled">
Next
</a>
Spusťte aplikaci a přejděte na stránku studentů.
- Pokud chcete zajistit, aby stránkování fungovalo, klikněte na stránkovací odkazy v různých pořadích řazení.
- Pokud chcete ověřit, že stránkování funguje správně s řazením a filtrováním, zadejte hledaný řetězec a zkuste stránkování.
Seskupení
Tato část vytvoří About
stránku, která zobrazí, kolik studentů se zaregistrovalo pro každé datum registrace. Aktualizace používá seskupení a zahrnuje následující kroky:
- Vytvořte model zobrazení pro data používaná stránkou
About
. About
Aktualizujte stránku tak, aby používala model zobrazení.
Vytvoření modelu zobrazení
Vytvořte složku Models/SchoolViewModels .
Vytvořte SchoolViewModels/EnrollmentDateGroup.cs
pomocí následujícího kódu:
using System;
using System.ComponentModel.DataAnnotations;
namespace ContosoUniversity.Models.SchoolViewModels
{
public class EnrollmentDateGroup
{
[DataType(DataType.Date)]
public DateTime? EnrollmentDate { get; set; }
public int StudentCount { get; set; }
}
}
Razor Vytvoření stránky
Vytvořte Pages/About.cshtml
soubor s následujícím kódem:
@page
@model ContosoUniversity.Pages.AboutModel
@{
ViewData["Title"] = "Student Body Statistics";
}
<h2>Student Body Statistics</h2>
<table>
<tr>
<th>
Enrollment Date
</th>
<th>
Students
</th>
</tr>
@foreach (var item in Model.Students)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.EnrollmentDate)
</td>
<td>
@item.StudentCount
</td>
</tr>
}
</table>
Vytvoření modelu stránky
Pages/About.cshtml.cs
Aktualizujte soubor následujícím kódem:
using ContosoUniversity.Models.SchoolViewModels;
using ContosoUniversity.Data;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ContosoUniversity.Models;
namespace ContosoUniversity.Pages
{
public class AboutModel : PageModel
{
private readonly SchoolContext _context;
public AboutModel(SchoolContext context)
{
_context = context;
}
public IList<EnrollmentDateGroup> Students { get; set; }
public async Task OnGetAsync()
{
IQueryable<EnrollmentDateGroup> data =
from student in _context.Students
group student by student.EnrollmentDate into dateGroup
select new EnrollmentDateGroup()
{
EnrollmentDate = dateGroup.Key,
StudentCount = dateGroup.Count()
};
Students = await data.AsNoTracking().ToListAsync();
}
}
}
Příkaz LINQ seskupí entity studentů podle data registrace, vypočítá počet entit v každé skupině a uloží výsledky do kolekce EnrollmentDateGroup
objektů modelu zobrazení.
Spusťte aplikaci a přejděte na stránku O aplikaci. Počet studentů pro každé datum registrace se zobrazí v tabulce.
Další kroky
V dalším kurzu aplikace používá migrace k aktualizaci datového modelu.
V tomto kurzu se přidá funkce řazení, filtrování, seskupování a stránkování.
Následující obrázek znázorňuje dokončenou stránku. Záhlaví sloupců se dají seřadit kliknutím na odkazy. Kliknutím na záhlaví sloupce se opakovaně přepne mezi vzestupným a sestupným pořadím řazení.
Pokud narazíte na problémy, které nemůžete vyřešit, stáhněte si dokončenou aplikaci.
Přidání řazení na indexovou stránku
Přidejte do Students/Index.cshtml.cs
PageModel
řetězce, které mají obsahovat parametry řazení:
public class IndexModel : PageModel
{
private readonly SchoolContext _context;
public IndexModel(SchoolContext context)
{
_context = context;
}
public string NameSort { get; set; }
public string DateSort { get; set; }
public string CurrentFilter { get; set; }
public string CurrentSort { get; set; }
Students/Index.cshtml.cs
OnGetAsync
Aktualizujte následující kód:
public async Task OnGetAsync(string sortOrder)
{
NameSort = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
DateSort = sortOrder == "Date" ? "date_desc" : "Date";
IQueryable<Student> studentIQ = from s in _context.Student
select s;
switch (sortOrder)
{
case "name_desc":
studentIQ = studentIQ.OrderByDescending(s => s.LastName);
break;
case "Date":
studentIQ = studentIQ.OrderBy(s => s.EnrollmentDate);
break;
case "date_desc":
studentIQ = studentIQ.OrderByDescending(s => s.EnrollmentDate);
break;
default:
studentIQ = studentIQ.OrderBy(s => s.LastName);
break;
}
Student = await studentIQ.AsNoTracking().ToListAsync();
}
Předchozí kód obdrží sortOrder
parametr z řetězce dotazu v adrese URL. Adresa URL (včetně řetězce dotazu) je generována pomocným pomocníkem značky ukotvení.
Parametr sortOrder
je "Name" nebo "Date". Parametr sortOrder
je volitelně následovaný "_desc" a určí sestupné pořadí. Výchozí pořadí řazení je vzestupné.
Když je stránka Index požadována z odkazu Studenti , neexistuje žádný řetězec dotazu. Studenti se zobrazují vzestupně podle příjmení. Vzestupné pořadí podle příjmení je výchozí (velká písmena) v switch
příkazu. Když uživatel klikne na odkaz záhlaví sloupce, zobrazí se v řetězcové hodnotě dotazu příslušná sortOrder
hodnota.
NameSort
a DateSort
stránka slouží Razor ke konfiguraci hypertextových odkazů záhlaví sloupců s příslušnými hodnotami řetězce dotazu:
public async Task OnGetAsync(string sortOrder)
{
NameSort = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
DateSort = sortOrder == "Date" ? "date_desc" : "Date";
IQueryable<Student> studentIQ = from s in _context.Student
select s;
switch (sortOrder)
{
case "name_desc":
studentIQ = studentIQ.OrderByDescending(s => s.LastName);
break;
case "Date":
studentIQ = studentIQ.OrderBy(s => s.EnrollmentDate);
break;
case "date_desc":
studentIQ = studentIQ.OrderByDescending(s => s.EnrollmentDate);
break;
default:
studentIQ = studentIQ.OrderBy(s => s.LastName);
break;
}
Student = await studentIQ.AsNoTracking().ToListAsync();
}
Následující kód obsahuje podmíněný operátor jazyka C#:
NameSort = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
DateSort = sortOrder == "Date" ? "date_desc" : "Date";
První řádek určuje, že pokud sortOrder
je hodnota null nebo prázdná, NameSort
je nastavena na hodnotu "name_desc". Pokud sortOrder
hodnota null nebo není prázdná, NameSort
nastaví se na prázdný řetězec.
Označuje se ?: operator
také jako ternární operátor.
Tyto dva příkazy umožňují stránce nastavit hypertextové odkazy záhlaví sloupců následujícím způsobem:
Aktuální pořadí řazení | Hypertextový odkaz na příjmení | Hypertextový odkaz na datum |
---|---|---|
Příjmení vzestupně | descending | ascending |
Příjmení sestupně | ascending | ascending |
Vzestupné datum | ascending | descending |
Datum sestupně | ascending | ascending |
Metoda používá LINQ to Entities k určení sloupce, podle který se má seřadit. Kód inicializuje IQueryable<Student>
před příkaz switch a upraví ho v příkazu switch:
public async Task OnGetAsync(string sortOrder)
{
NameSort = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
DateSort = sortOrder == "Date" ? "date_desc" : "Date";
IQueryable<Student> studentIQ = from s in _context.Student
select s;
switch (sortOrder)
{
case "name_desc":
studentIQ = studentIQ.OrderByDescending(s => s.LastName);
break;
case "Date":
studentIQ = studentIQ.OrderBy(s => s.EnrollmentDate);
break;
case "date_desc":
studentIQ = studentIQ.OrderByDescending(s => s.EnrollmentDate);
break;
default:
studentIQ = studentIQ.OrderBy(s => s.LastName);
break;
}
Student = await studentIQ.AsNoTracking().ToListAsync();
}
IQueryable
Při vytvoření nebo úpravě se do databáze neposílají žádné dotazy. Dotaz se nespustí, dokud IQueryable
se objekt nepřevedí do kolekce. IQueryable
jsou převedeny na kolekci voláním metody, například ToListAsync
. IQueryable
Výsledkem kódu je tedy jeden dotaz, který se nespustí, dokud nebude následující příkaz:
Student = await studentIQ.AsNoTracking().ToListAsync();
OnGetAsync
mohlo by se dostat do podrobného formátu s velkým počtem seřaditelných sloupců.
Přidání hypertextových odkazů záhlaví sloupců na stránku Index studenta
Nahraďte kód v Students/Index.cshtml
, následujícím zvýrazněným kódem:
@page
@model ContosoUniversity.Pages.Students.IndexModel
@{
ViewData["Title"] = "Index";
}
<h2>Index</h2>
<p>
<a asp-page="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
<a asp-page="./Index" asp-route-sortOrder="@Model.NameSort">
@Html.DisplayNameFor(model => model.Student[0].LastName)
</a>
</th>
<th>
@Html.DisplayNameFor(model => model.Student[0].FirstMidName)
</th>
<th>
<a asp-page="./Index" asp-route-sortOrder="@Model.DateSort">
@Html.DisplayNameFor(model => model.Student[0].EnrollmentDate)
</a>
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model.Student)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
@Html.DisplayFor(modelItem => item.FirstMidName)
</td>
<td>
@Html.DisplayFor(modelItem => item.EnrollmentDate)
</td>
<td>
<a asp-page="./Edit" asp-route-id="@item.ID">Edit</a> |
<a asp-page="./Details" asp-route-id="@item.ID">Details</a> |
<a asp-page="./Delete" asp-route-id="@item.ID">Delete</a>
</td>
</tr>
}
</tbody>
</table>
Předchozí kód:
- Přidá hypertextové odkazy do
LastName
záhlaví sloupců aEnrollmentDate
sloupců. - Používá informace v
NameSort
aDateSort
k nastavení hypertextových odkazů s aktuálními hodnotami pořadí řazení.
Ověření, že řazení funguje:
- Spusťte aplikaci a vyberte kartu Studenti .
- Klikněte na příjmení.
- Klikněte na Datum registrace.
Lepší porozumění kódu:
- Nastavte
Students/Index.cshtml.cs
zarážku naswitch (sortOrder)
. - Přidejte kukátku
NameSort
aDateSort
. - Nastavte
Students/Index.cshtml
zarážku na@Html.DisplayNameFor(model => model.Student[0].LastName)
.
Projděte ladicí program.
Přidání vyhledávacího pole na stránku Index studentů
Přidání filtrování na stránku Index studentů:
- Na stránku se přidá Razor textové pole a tlačítko odeslat. Textové pole poskytuje hledaný řetězec na křestní jméno nebo příjmení.
- Model stránky se aktualizuje tak, aby používal hodnotu textového pole.
Přidání funkce filtrování do metody Index
Students/Index.cshtml.cs
OnGetAsync
Aktualizujte následující kód:
public async Task OnGetAsync(string sortOrder, string searchString)
{
NameSort = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
DateSort = sortOrder == "Date" ? "date_desc" : "Date";
CurrentFilter = searchString;
IQueryable<Student> studentIQ = from s in _context.Student
select s;
if (!String.IsNullOrEmpty(searchString))
{
studentIQ = studentIQ.Where(s => s.LastName.Contains(searchString)
|| s.FirstMidName.Contains(searchString));
}
switch (sortOrder)
{
case "name_desc":
studentIQ = studentIQ.OrderByDescending(s => s.LastName);
break;
case "Date":
studentIQ = studentIQ.OrderBy(s => s.EnrollmentDate);
break;
case "date_desc":
studentIQ = studentIQ.OrderByDescending(s => s.EnrollmentDate);
break;
default:
studentIQ = studentIQ.OrderBy(s => s.LastName);
break;
}
Student = await studentIQ.AsNoTracking().ToListAsync();
}
Předchozí kód:
searchString
Přidá parametr doOnGetAsync
metody. Hodnota vyhledávacího řetězce se přijímá z textového pole přidaného v další části.- Přidáno do příkazu LINQ
Where
klauzule. KlauzuleWhere
vybere jenom studenty, jejichž křestní jméno nebo příjmení obsahuje hledaný řetězec. Příkaz LINQ se spustí pouze v případě, že je k dispozici hodnota, kterou chcete vyhledat.
Poznámka: Předchozí kód volá metodu Where
objektu IQueryable
a filtr je zpracován na serveru. V některých scénářích může aplikace volat metodu Where
jako metodu rozšíření v kolekci v paměti. Předpokládejme například, že _context.Students
se změny z EF CoreDbSet
metody úložiště, která vrací kolekci IEnumerable
. Výsledek by obvykle byl stejný, ale v některých případech se může lišit.
Například implementace Contains
rozhraní .NET Framework ve výchozím nastavení provádí porovnání s rozlišováním velkých a malých písmen. V SYSTÉMU SQL Server Contains
je citlivost na velká a malá písmena určena nastavením kolace instance SYSTÉMU SQL Server. SQL Server ve výchozím nastavení nerozlišuje malá a velká písmena. ToUpper
lze volat, aby test explicitně nerozlišil malá a velká písmena:
Where(s => s.LastName.ToUpper().Contains(searchString.ToUpper())
Předchozí kód zajistí, aby výsledky nerozlišovaly malá a velká písmena, pokud se kód změní na použití IEnumerable
. Při Contains
zavolání kolekce IEnumerable
se použije implementace .NET Core. Při Contains
zavolání objektu IQueryable
se použije implementace databáze. Vrácení IEnumerable
z úložiště může mít významné snížení výkonu:
- Všechny řádky se vrátí ze serveru DATABÁZE.
- Filtr se použije na všechny vrácené řádky v aplikaci.
Je tu trest za výkon za volání ToUpper
. Kód ToUpper
přidá funkci v klauzuli WHERE příkazu TSQL SELECT. Přidaná funkce brání optimalizátoru v používání indexu. Vzhledem k tomu, že sql je nainstalovaný jako nerozlišující velká a malá písmena, je nejlepší se vyhnout ToUpper
volání, když není potřeba.
Přidání vyhledávacího pole na stránku Index studenta
Do Pages/Students/Index.cshtml
pole přidejte následující zvýrazněný kód pro vytvoření tlačítka Hledat a seřazeného chromu.
@page
@model ContosoUniversity.Pages.Students.IndexModel
@{
ViewData["Title"] = "Index";
}
<h2>Index</h2>
<p>
<a asp-page="Create">Create New</a>
</p>
<form asp-page="./Index" method="get">
<div class="form-actions no-color">
<p>
Find by name:
<input type="text" name="SearchString" value="@Model.CurrentFilter" />
<input type="submit" value="Search" class="btn btn-default" /> |
<a asp-page="./Index">Back to full List</a>
</p>
</div>
</form>
<table class="table">
Předchozí kód pomocí pomocné rutiny <form>
značky přidá vyhledávací textové pole a tlačítko. Ve výchozím nastavení <form>
pomocník značky odesílá data formuláře pomocí post. U post se parametry předávají v textu zprávy HTTP, a ne v adrese URL. Při použití http GET se data formuláře předávají v adrese URL jako řetězce dotazu. Předání dat pomocí řetězců dotazu umožňuje uživatelům vytvořit záložku adresy URL. Pokyny W3C doporučují, aby se funkce GET používala, když akce nemá za následek aktualizaci.
Otestujte aplikaci:
- Vyberte kartu Studenti a zadejte hledaný řetězec.
- Vyberte Hledat.
Všimněte si, že adresa URL obsahuje hledaný řetězec.
http://localhost:5000/Students?SearchString=an
Pokud je stránka záložka, záložka obsahuje adresu URL stránky a SearchString
řetězec dotazu. V method="get"
značce form
je to, co způsobilo vygenerování řetězce dotazu.
Když je v současné době vybrán odkaz pro řazení záhlaví sloupce, hodnota filtru z vyhledávacího pole se ztratí. Ztracená hodnota filtru je opravena v další části.
Přidání funkce stránkování na stránku Index studentů
V této části se vytvoří třída, PaginatedList
která podporuje stránkování. Třída PaginatedList
používá Skip
a Take
příkazy k filtrování dat na serveru místo načtení všech řádků tabulky. Následující obrázek znázorňuje stránkovací tlačítka.
Ve složce projektu vytvořte PaginatedList.cs
následující kód:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
namespace ContosoUniversity
{
public class PaginatedList<T> : List<T>
{
public int PageIndex { get; private set; }
public int TotalPages { get; private set; }
public PaginatedList(List<T> items, int count, int pageIndex, int pageSize)
{
PageIndex = pageIndex;
TotalPages = (int)Math.Ceiling(count / (double)pageSize);
this.AddRange(items);
}
public bool HasPreviousPage => PageIndex > 1;
public bool HasNextPage => PageIndex < TotalPages;
public static async Task<PaginatedList<T>> CreateAsync(
IQueryable<T> source, int pageIndex, int pageSize)
{
var count = await source.CountAsync();
var items = await source.Skip(
(pageIndex - 1) * pageSize)
.Take(pageSize).ToListAsync();
return new PaginatedList<T>(items, count, pageIndex, pageSize);
}
}
}
Metoda CreateAsync
v předchozím kódu má velikost stránky a číslo stránky a použije příslušné Skip
příkazy a Take
příkazy na IQueryable
. Při ToListAsync
zavolání na této IQueryable
stránce vrátí seznam obsahující pouze požadovanou stránku. Vlastnosti HasPreviousPage
a HasNextPage
slouží k povolení nebo zakázání předchozí a další stránkovací tlačítka.
Metoda CreateAsync
se používá k vytvoření PaginatedList<T>
. Konstruktor nemůže vytvořit PaginatedList<T>
objekt, konstruktory nemůžou spustit asynchronní kód.
Přidání funkce stránkování do metody Index
V Students/Index.cshtml.cs
aplikaci aktualizujte typ Student
z IList<Student>
na PaginatedList<Student>
:
public PaginatedList<Student> Student { get; set; }
Students/Index.cshtml.cs
OnGetAsync
Aktualizujte následující kód:
public async Task OnGetAsync(string sortOrder,
string currentFilter, string searchString, int? pageIndex)
{
CurrentSort = sortOrder;
NameSort = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
DateSort = sortOrder == "Date" ? "date_desc" : "Date";
if (searchString != null)
{
pageIndex = 1;
}
else
{
searchString = currentFilter;
}
CurrentFilter = searchString;
IQueryable<Student> studentIQ = from s in _context.Student
select s;
if (!String.IsNullOrEmpty(searchString))
{
studentIQ = studentIQ.Where(s => s.LastName.Contains(searchString)
|| s.FirstMidName.Contains(searchString));
}
switch (sortOrder)
{
case "name_desc":
studentIQ = studentIQ.OrderByDescending(s => s.LastName);
break;
case "Date":
studentIQ = studentIQ.OrderBy(s => s.EnrollmentDate);
break;
case "date_desc":
studentIQ = studentIQ.OrderByDescending(s => s.EnrollmentDate);
break;
default:
studentIQ = studentIQ.OrderBy(s => s.LastName);
break;
}
int pageSize = 3;
Student = await PaginatedList<Student>.CreateAsync(
studentIQ.AsNoTracking(), pageIndex ?? 1, pageSize);
}
Předchozí kód přidá index stránky, aktuální sortOrder
a do currentFilter
podpisu metody.
public async Task OnGetAsync(string sortOrder,
string currentFilter, string searchString, int? pageIndex)
Všechny parametry mají hodnotu null v následujících případech:
- Stránka se volá z odkazu Studenti .
- Uživatel neklikli na stránkovací nebo seřazovací odkaz.
Po kliknutí na stránkovací odkaz obsahuje proměnná indexu stránky číslo stránky, které se má zobrazit.
CurrentSort
Razor poskytuje stránku s aktuálním pořadím řazení. Aktuální pořadí řazení musí být zahrnuto ve stránkovacích odkazech, aby bylo možné zachovat pořadí řazení při stránkování.
CurrentFilter
Razor poskytuje stránku s aktuálním řetězcem filtru. Hodnota CurrentFilter
:
- Aby bylo možné zachovat nastavení filtru během stránkování, musí být součástí stránkovacího odkazu.
- Po opětovném zobrazení stránky je nutné obnovit textové pole do textového pole.
Pokud se při stránkování změní hledaný řetězec, stránka se resetuje na 1. Stránka se musí resetovat na 1, protože nový filtr může vést k zobrazení různých dat. Když je zadána hodnota hledání a je vybrána možnost Odeslat :
- Vyhledávací řetězec se změní.
- Parametr
searchString
nemá hodnotu null.
if (searchString != null)
{
pageIndex = 1;
}
else
{
searchString = currentFilter;
}
Metoda PaginatedList.CreateAsync
převede dotaz studenta na jednu stránku studentů v typu kolekce, která podporuje stránkování. Tato jedna stránka studentů se předá na Razor stránku.
Student = await PaginatedList<Student>.CreateAsync(
studentIQ.AsNoTracking(), pageIndex ?? 1, pageSize);
Dvě otazníky představují PaginatedList.CreateAsync
operátor nulového sjednocení. Operátor nulového sjednocení definuje výchozí hodnotu pro typ s možnou hodnotou null. Výraz (pageIndex ?? 1)
znamená, že vrátí hodnotu pageIndex
, pokud má hodnotu. Pokud pageIndex
nemá hodnotu, vrátí hodnotu 1.
Přidání stránkovacího odkazu na stránku studenta Razor
Aktualizujte kód v Students/Index.cshtml
souboru . Změny jsou zvýrazněné:
@page
@model ContosoUniversity.Pages.Students.IndexModel
@{
ViewData["Title"] = "Index";
}
<h2>Index</h2>
<p>
<a asp-page="Create">Create New</a>
</p>
<form asp-page="./Index" method="get">
<div class="form-actions no-color">
<p>
Find by name: <input type="text" name="SearchString" value="@Model.CurrentFilter" />
<input type="submit" value="Search" class="btn btn-default" /> |
<a asp-page="./Index">Back to full List</a>
</p>
</div>
</form>
<table class="table">
<thead>
<tr>
<th>
<a asp-page="./Index" asp-route-sortOrder="@Model.NameSort"
asp-route-currentFilter="@Model.CurrentFilter">
@Html.DisplayNameFor(model => model.Student[0].LastName)
</a>
</th>
<th>
@Html.DisplayNameFor(model => model.Student[0].FirstMidName)
</th>
<th>
<a asp-page="./Index" asp-route-sortOrder="@Model.DateSort"
asp-route-currentFilter="@Model.CurrentFilter">
@Html.DisplayNameFor(model => model.Student[0].EnrollmentDate)
</a>
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model.Student)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
@Html.DisplayFor(modelItem => item.FirstMidName)
</td>
<td>
@Html.DisplayFor(modelItem => item.EnrollmentDate)
</td>
<td>
<a asp-page="./Edit" asp-route-id="@item.ID">Edit</a> |
<a asp-page="./Details" asp-route-id="@item.ID">Details</a> |
<a asp-page="./Delete" asp-route-id="@item.ID">Delete</a>
</td>
</tr>
}
</tbody>
</table>
@{
var prevDisabled = !Model.Student.HasPreviousPage ? "disabled" : "";
var nextDisabled = !Model.Student.HasNextPage ? "disabled" : "";
}
<a asp-page="./Index"
asp-route-sortOrder="@Model.CurrentSort"
asp-route-pageIndex="@(Model.Student.PageIndex - 1)"
asp-route-currentFilter="@Model.CurrentFilter"
class="btn btn-default @prevDisabled">
Previous
</a>
<a asp-page="./Index"
asp-route-sortOrder="@Model.CurrentSort"
asp-route-pageIndex="@(Model.Student.PageIndex + 1)"
asp-route-currentFilter="@Model.CurrentFilter"
class="btn btn-default @nextDisabled">
Next
</a>
Odkazy záhlaví sloupce používají řetězec dotazu k předání aktuálního vyhledávacího OnGetAsync
řetězce metodě, aby uživatel mohl řadit ve výsledcích filtru:
<a asp-page="./Index" asp-route-sortOrder="@Model.NameSort"
asp-route-currentFilter="@Model.CurrentFilter">
@Html.DisplayNameFor(model => model.Student[0].LastName)
</a>
Stránkovací tlačítka se zobrazují pomocnými rutiny značek:
<a asp-page="./Index"
asp-route-sortOrder="@Model.CurrentSort"
asp-route-pageIndex="@(Model.Student.PageIndex - 1)"
asp-route-currentFilter="@Model.CurrentFilter"
class="btn btn-default @prevDisabled">
Previous
</a>
<a asp-page="./Index"
asp-route-sortOrder="@Model.CurrentSort"
asp-route-pageIndex="@(Model.Student.PageIndex + 1)"
asp-route-currentFilter="@Model.CurrentFilter"
class="btn btn-default @nextDisabled">
Next
</a>
Spusťte aplikaci a přejděte na stránku studentů.
- Pokud chcete zajistit, aby stránkování fungovalo, klikněte na stránkovací odkazy v různých pořadích řazení.
- Pokud chcete ověřit, že stránkování funguje správně s řazením a filtrováním, zadejte hledaný řetězec a zkuste stránkování.
Lepší porozumění kódu:
- Nastavte
Students/Index.cshtml.cs
zarážku naswitch (sortOrder)
. - Přidejte kukátku ,
NameSort
DateSort
,CurrentSort
aModel.Student.PageIndex
. - Nastavte
Students/Index.cshtml
zarážku na@Html.DisplayNameFor(model => model.Student[0].LastName)
.
Projděte ladicí program.
Aktualizace stránky O aplikaci tak, aby zobrazovala statistiky studentů
V tomto kroku se aktualizuje, aby se zobrazilo, Pages/About.cshtml
kolik studentů se zaregistrovalo pro každé datum registrace. Aktualizace používá seskupení a zahrnuje následující kroky:
- Vytvořte model zobrazení pro data používaná stránkou O aplikaci.
- Aktualizujte stránku O aplikaci tak, aby používala model zobrazení.
Vytvoření modelu zobrazení
Ve složce Models vytvořte složku SchoolViewModels.
Do složky SchoolViewModels přidejte následující EnrollmentDateGroup.cs
kód:
using System;
using System.ComponentModel.DataAnnotations;
namespace ContosoUniversity.Models.SchoolViewModels
{
public class EnrollmentDateGroup
{
[DataType(DataType.Date)]
public DateTime? EnrollmentDate { get; set; }
public int StudentCount { get; set; }
}
}
Aktualizace modelu stránky O produktu
Webové šablony v ASP.NET Core 2.2 nezahrnují stránku O produktu. Pokud používáte ASP.NET Core 2.2, vytvořte stránku O Razor aplikaci.
Pages/About.cshtml.cs
Aktualizujte soubor následujícím kódem:
using ContosoUniversity.Models.SchoolViewModels;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ContosoUniversity.Models;
namespace ContosoUniversity.Pages
{
public class AboutModel : PageModel
{
private readonly SchoolContext _context;
public AboutModel(SchoolContext context)
{
_context = context;
}
public IList<EnrollmentDateGroup> Student { get; set; }
public async Task OnGetAsync()
{
IQueryable<EnrollmentDateGroup> data =
from student in _context.Student
group student by student.EnrollmentDate into dateGroup
select new EnrollmentDateGroup()
{
EnrollmentDate = dateGroup.Key,
StudentCount = dateGroup.Count()
};
Student = await data.AsNoTracking().ToListAsync();
}
}
}
Příkaz LINQ seskupí entity studentů podle data registrace, vypočítá počet entit v každé skupině a uloží výsledky do kolekce EnrollmentDateGroup
objektů modelu zobrazení.
Úprava stránky O Razor aplikaci
Nahraďte kód v Pages/About.cshtml
souboru následujícím kódem:
@page
@model ContosoUniversity.Pages.AboutModel
@{
ViewData["Title"] = "Student Body Statistics";
}
<h2>Student Body Statistics</h2>
<table>
<tr>
<th>
Enrollment Date
</th>
<th>
Students
</th>
</tr>
@foreach (var item in Model.Student)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.EnrollmentDate)
</td>
<td>
@item.StudentCount
</td>
</tr>
}
</table>
Spusťte aplikaci a přejděte na stránku O aplikaci. Počet studentů pro každé datum registrace se zobrazí v tabulce.
Pokud narazíte na problémy, které nemůžete vyřešit, stáhněte si dokončenou aplikaci pro tuto fázi.
Další materiály
V dalším kurzu aplikace používá migrace k aktualizaci datového modelu.
Váš názor
https://aka.ms/ContentUserFeedback.
Připravujeme: V průběhu roku 2024 budeme postupně vyřazovat problémy z GitHub coby mechanismus zpětné vazby pro obsah a nahrazovat ho novým systémem zpětné vazby. Další informace naleznete v tématu:Odeslat a zobrazit názory pro