Bagian 3, Razor Halaman dengan EF Core inti ASP.NET - Urutkan, Filter, Halaman
Oleh Tom Dykstra, Jeremy Likness, dan Jon P Smith
Aplikasi web Contoso University menunjukkan cara membuat Razor aplikasi web Pages menggunakan EF Core dan Visual Studio. Untuk informasi tentang seri tutorial, lihat tutorial pertama.
Jika Anda mengalami masalah yang tidak dapat Anda selesaikan, unduh aplikasi yang telah selesai dan bandingkan kode tersebut dengan apa yang Anda buat dengan mengikuti tutorial.
Tutorial ini menambahkan fungsi pengurutan, pemfilteran, dan penomoran halaman ke halaman Siswa.
Ilustrasi berikut ini memperlihatkan halaman yang telah selesai. Judul kolom adalah tautan yang dapat diklik untuk mengurutkan kolom. Klik judul kolom berulang kali untuk beralih antara urutan urutan naik dan turun.
Menambahkan pengurutan
Ganti kode dengan Pages/Students/Index.cshtml.cs
kode berikut untuk menambahkan pengurutan.
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();
}
}
Kode sebelumnya:
- Memerlukan penambahan
using System;
. - Menambahkan properti untuk memuat parameter pengurutan.
- Mengubah nama properti menjadi
Student
Students
. - Mengganti kode dalam
OnGetAsync
metode .
Metode OnGetAsync
menerima sortOrder
parameter dari string kueri di URL. URL dan string kueri dihasilkan oleh Pembantu Tag Jangkar.
Parameternya sortOrder
adalah Name
atau Date
. Parameter sortOrder
ini secara opsional diikuti oleh _desc
untuk menentukan urutan menurut. Urutan sortir default adalah menaik.
Saat halaman Indeks diminta dari tautan Siswa , tidak ada string kueri. Siswa ditampilkan dalam urutan naik berdasarkan nama belakang. Urutan naik menurut nama belakang adalah default
dalam switch
pernyataan. Saat pengguna mengklik tautan judul kolom, nilai yang sesuai sortOrder
disediakan dalam nilai string kueri.
NameSort
dan DateSort
digunakan oleh Razor Halaman untuk mengonfigurasi hyperlink judul kolom dengan nilai string kueri yang sesuai:
NameSort = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
DateSort = sortOrder == "Date" ? "date_desc" : "Date";
Kode menggunakan operator kondisi C# ?:. Operator ?:
adalah operator ternary, dibutuhkan tiga operand. Baris pertama menentukan bahwa ketika sortOrder
null atau kosong, NameSort
diatur ke name_desc
. Jika sortOrder
tidak null atau kosong, NameSort
diatur ke string kosong.
Kedua pernyataan ini memungkinkan halaman untuk mengatur hyperlink judul kolom sebagai berikut:
Urutan pengurutan saat ini | Hyperlink Nama Belakang | Hyperlink Tanggal |
---|---|---|
Nama Belakang naik | Urut turun | urutan naik |
Nama Belakang turun | urutan naik | urutan naik |
Tanggal naik | urutan naik | Urut turun |
Tanggal turun | urutan naik | urutan naik |
Metode ini menggunakan LINQ ke Entitas untuk menentukan kolom yang akan diurutkan. Kode menginisialisasi IQueryable<Student>
sebelum pernyataan pengalihan, dan memodifikasinya dalam pernyataan pengalihan:
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
Saat dibuat atau dimodifikasi, tidak ada kueri yang dikirim ke database. Kueri tidak dijalankan sampai objek dikonversi IQueryable
menjadi koleksi. IQueryable
dikonversi ke koleksi dengan memanggil metode seperti ToListAsync
. Oleh karena itu, IQueryable
kode menghasilkan satu kueri yang tidak dijalankan hingga pernyataan berikut:
Students = await studentsIQ.AsNoTracking().ToListAsync();
OnGetAsync
bisa mendapatkan verbose dengan sejumlah besar kolom yang dapat diurutkan. Untuk informasi tentang cara alternatif untuk mengodekan fungsionalitas ini, lihat Menggunakan LINQ dinamis untuk menyederhanakan kode dalam versi MVC dari seri tutorial ini.
Menambahkan hyperlink judul kolom ke halaman Indeks Siswa
Ganti kode di Students/Index.cshtml
, dengan kode berikut. Perubahan disorot.
@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>
Kode sebelumnya:
- Menambahkan hyperlink ke
LastName
judul kolom danEnrollmentDate
. - Menggunakan informasi dalam
NameSort
danDateSort
untuk menyiapkan hyperlink dengan nilai susunan urutan saat ini. - Mengubah judul halaman dari Indeks ke Siswa.
- Perubahan
Model.Student
padaModel.Students
.
Untuk memverifikasi bahwa pengurutan berfungsi:
- Jalankan aplikasi dan pilih tab Siswa .
- Klik judul kolom.
Menambahkan pemfilteran
Untuk menambahkan pemfilteran ke halaman Indeks Siswa:
- Kotak teks dan tombol kirim ditambahkan ke Razor Halaman. Kotak teks menyediakan string pencarian pada nama depan atau belakang.
- Model halaman diperbarui untuk menggunakan nilai kotak teks.
Memperbarui metode OnGetAsync
Ganti kode dengan Students/Index.cshtml.cs
kode berikut untuk menambahkan pemfilteran:
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();
}
}
Kode sebelumnya:
searchString
Menambahkan parameter keOnGetAsync
metode , dan menyimpan nilai parameter dalamCurrentFilter
properti . Nilai string pencarian diterima dari kotak teks yang ditambahkan di bagian berikutnya.- Menambahkan ke klausa pernyataan
Where
LINQ. KlausaWhere
hanya memilih siswa yang nama depan atau nama belakangnya berisi string pencarian. Pernyataan LINQ dijalankan hanya jika ada nilai untuk dicari.
IQueryable vs. IEnumerable
Kode memanggil metode pada IQueryable
objek, dan filter diproses Where di server. Dalam beberapa skenario, aplikasi mungkin memanggil Where
metode sebagai metode ekstensi pada koleksi dalam memori. Misalnya, misalkan _context.Students
perubahan dari EF CoreDbSet
ke metode repositori yang mengembalikan IEnumerable
koleksi. Hasilnya biasanya akan sama tetapi dalam beberapa kasus mungkin berbeda.
Misalnya, implementasi .NET Framework melakukan perbandingan Contains
peka huruf besar/kecil secara default. Di SQL Server, Contains
sensitivitas huruf besar/kecil ditentukan oleh pengaturan kolase instans SQL Server. SQL Server default menjadi tidak peka huruf besar/kecil. SQLite default ke peka huruf besar/kecil. ToUpper
dapat dipanggil untuk membuat pengujian secara eksplisit tidak peka huruf besar/kecil:
Where(s => s.LastName.ToUpper().Contains(searchString.ToUpper())`
Kode sebelumnya akan memastikan bahwa filter tidak peka huruf besar/kecil meskipun Where
metode dipanggil pada IEnumerable
atau berjalan di SQLite.
Ketika Contains
dipanggil pada IEnumerable
koleksi, implementasi .NET Core digunakan. Ketika Contains
dipanggil pada IQueryable
objek, implementasi database digunakan.
Panggilan Contains
pada IQueryable
biasanya lebih disukai karena alasan performa. Dengan IQueryable
, pemfilteran dilakukan oleh server database. IEnumerable
Jika dibuat terlebih dahulu, semua baris harus dikembalikan dari server database.
Ada penalti performa untuk memanggil ToUpper
. Kode ToUpper
menambahkan fungsi dalam klausa WHERE dari pernyataan TSQL SELECT. Fungsi yang ditambahkan mencegah pengoptimal menggunakan indeks. Mengingat bahwa SQL diinstal sebagai tidak peka huruf besar/kecil, sebaiknya hindari ToUpper
panggilan saat tidak diperlukan.
Untuk informasi selengkapnya, lihat Cara menggunakan kueri yang tidak peka huruf besar/kecil dengan penyedia Sqlite.
Razor Memperbarui halaman
Ganti kode di Pages/Students/Index.cshtml
untuk menambahkan tombol Cari .
@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>
Kode sebelumnya menggunakan pembantu <form>
tag untuk menambahkan kotak teks pencarian dan tombol. Secara default, pembantu <form>
tag mengirimkan data formulir dengan POST. Dengan POST, parameter diteruskan dalam isi pesan HTTP dan bukan di URL. Saat HTTP GET digunakan, data formulir diteruskan di URL sebagai string kueri. Meneruskan data dengan string kueri memungkinkan pengguna untuk menandai URL. Panduan W3C merekomendasikan bahwa GET harus digunakan saat tindakan tidak menghasilkan pembaruan.
Uji aplikasi:
Pilih tab Siswa dan masukkan string pencarian. Jika Anda menggunakan SQLite, filter tidak peka huruf besar/kecil hanya jika Anda menerapkan kode opsional
ToUpper
yang ditampilkan sebelumnya.Pilih Telusuri.
Perhatikan bahwa URL berisi string pencarian. Contohnya:
https://localhost:5001/Students?SearchString=an
Jika halaman diberi marka buku, marka buku berisi URL ke halaman dan SearchString
string kueri. Dalam method="get"
form
tag adalah apa yang menyebabkan string kueri dihasilkan.
Saat ini, saat tautan pengurutan judul kolom dipilih, nilai filter dari kotak Pencarian hilang. Nilai filter yang hilang diperbaiki di bagian berikutnya.
Tambahkan halaman
Di bagian ini, PaginatedList
kelas dibuat untuk mendukung halaman. Kelas PaginatedList
menggunakan Skip
pernyataan dan Take
untuk memfilter data di server alih-alih mengambil semua baris tabel. Ilustrasi berikut menunjukkan tombol halaman.
Membuat kelas PaginatedList
Di folder proyek, buat PaginatedList.cs
dengan kode berikut:
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);
}
}
}
Metode CreateAsync
dalam kode sebelumnya mengambil ukuran halaman dan nomor halaman dan menerapkan pernyataan dan Take
yang sesuai Skip
ke IQueryable
. Ketika ToListAsync
dipanggil pada IQueryable
, itu mengembalikan Daftar yang hanya berisi halaman yang diminta. Properti HasPreviousPage
dan HasNextPage
digunakan untuk mengaktifkan atau menonaktifkan tombol halaman Sebelumnya dan Berikutnya .
Metode CreateAsync
ini digunakan untuk membuat PaginatedList<T>
. Konstruktor tidak dapat membuat PaginatedList<T>
objek; konstruktor tidak dapat menjalankan kode asinkron.
Menambahkan ukuran halaman ke konfigurasi
Tambahkan PageSize
ke appsettings.json
file Konfigurasi :
{
"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"
}
}
Menambahkan halaman ke IndexModel
Ganti kode di Students/Index.cshtml.cs
untuk menambahkan halaman.
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);
}
}
}
Kode sebelumnya:
- Mengubah jenis
Students
properti dariIList<Student>
kePaginatedList<Student>
. - Menambahkan indeks halaman, , dan
currentFilter
OnGetAsync
ke tanda tangan metode saat inisortOrder
. - Menyimpan urutan pengurutan
CurrentSort
dalam properti . - Mereset indeks halaman ke 1 saat ada string pencarian baru.
PaginatedList
Menggunakan kelas untuk mendapatkan entitas Siswa.pageSize
Atur ke 3 dari Konfigurasi, 4 jika konfigurasi gagal.
Semua parameter yang OnGetAsync
diterima null ketika:
- Halaman dipanggil dari tautan Siswa .
- Pengguna belum mengklik tautan penomoran atau pengurutan.
Saat tautan halaman diklik, variabel indeks halaman berisi nomor halaman yang akan ditampilkan.
Properti CurrentSort
menyediakan Razor Halaman dengan urutan pengurutan saat ini. Urutan pengurutan saat ini harus disertakan dalam tautan halaman untuk mempertahankan urutan pengurutan saat penomoran halaman.
Properti CurrentFilter
menyediakan Razor Halaman dengan string filter saat ini. Nilai CurrentFilter
:
- Harus disertakan dalam tautan halaman untuk mempertahankan pengaturan filter selama halaman.
- Harus dipulihkan ke kotak teks ketika halaman diputar ulang.
Jika string pencarian diubah saat halaman, halaman diatur ulang ke 1. Halaman harus direset ke 1 karena filter baru dapat menghasilkan data yang berbeda untuk ditampilkan. Saat nilai pencarian dimasukkan dan Kirim dipilih:
- String pencarian diubah.
- Parameter
searchString
tidak null.
Metode ini PaginatedList.CreateAsync
mengonversi kueri siswa menjadi satu halaman siswa dalam jenis koleksi yang mendukung halaman. Halaman tunggal siswa tersebut diteruskan ke Razor Halaman.
Dua tanda tanya setelah pageIndex
dalam PaginatedList.CreateAsync
panggilan mewakili operator null-coalescing. Operator null-coalescing mendefinisikan nilai default untuk jenis nullable. Ekspresi pageIndex ?? 1
mengembalikan nilai pageIndex
jika memiliki nilai, jika tidak, ekspresi mengembalikan 1.
Menambahkan tautan halaman
Ganti kode dengan Students/Index.cshtml
kode berikut. Perubahan disorot:
@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>
Tautan header kolom menggunakan string kueri untuk meneruskan string pencarian saat ini ke OnGetAsync
metode :
<a asp-page="./Index" asp-route-sortOrder="@Model.NameSort"
asp-route-currentFilter="@Model.CurrentFilter">
@Html.DisplayNameFor(model => model.Students[0].LastName)
</a>
Tombol halaman ditampilkan oleh pembantu tag:
<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>
Jalankan aplikasi dan navigasikan ke halaman siswa.
- Untuk memastikan penomoran berfungsi, klik tautan halaman dalam urutan pengurutan yang berbeda.
- Untuk memverifikasi bahwa penomoran berfungsi dengan benar dengan pengurutan dan pemfilteran, masukkan string pencarian dan coba penomoran halaman.
Pengelompokan
Bagian ini membuat About
halaman yang menampilkan berapa banyak siswa yang telah mendaftar untuk setiap tanggal pendaftaran. Pembaruan menggunakan pengelompokan dan menyertakan langkah-langkah berikut:
- Buat model tampilan untuk data yang digunakan oleh
About
halaman. About
Perbarui halaman untuk menggunakan model tampilan.
Membuat model tampilan
Buat folder Models/SchoolViewModels .
Buat SchoolViewModels/EnrollmentDateGroup.cs
dengan kode berikut:
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 Membuat Halaman
Buat Pages/About.cshtml
file dengan kode berikut:
@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>
Membuat model halaman
Pages/About.cshtml.cs
Perbarui file dengan kode berikut:
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();
}
}
}
Pernyataan LINQ mengelompokkan entitas siswa berdasarkan tanggal pendaftaran, menghitung jumlah entitas di setiap grup, dan menyimpan hasilnya dalam kumpulan EnrollmentDateGroup
objek model tampilan.
Jalankan aplikasi dan navigasikan ke halaman Tentang. Jumlah siswa untuk setiap tanggal pendaftaran ditampilkan dalam tabel.
Langkah berikutnya
Dalam tutorial berikutnya, aplikasi menggunakan migrasi untuk memperbarui model data.
Dalam tutorial ini, pengurutan, pemfilteran, pengelompokan, dan penomoran, fungsionalitas ditambahkan.
Ilustrasi berikut ini memperlihatkan halaman yang telah selesai. Judul kolom adalah tautan yang dapat diklik untuk mengurutkan kolom. Mengklik judul kolom berulang kali beralih antara urutan urutan naik dan turun.
Jika Anda mengalami masalah yang tidak dapat Anda selesaikan, unduh aplikasi yang telah selesai.
Menambahkan pengurutan ke halaman Indeks
Tambahkan string ke Students/Index.cshtml.cs
PageModel
untuk berisi parameter pengurutan:
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
Perbarui dengan kode berikut:
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();
}
Kode sebelumnya menerima sortOrder
parameter dari string kueri di URL. URL (termasuk string kueri) dihasilkan oleh Pembantu Tag Jangkar
Parameternya sortOrder
adalah "Nama" atau "Tanggal." Parameter sortOrder
secara opsional diikuti oleh "_desc" untuk menentukan urutan turun. Urutan sortir default adalah menaik.
Saat halaman Indeks diminta dari tautan Siswa , tidak ada string kueri. Siswa ditampilkan dalam urutan naik berdasarkan nama belakang. Urutan naik menurut nama belakang adalah default (kasus fall-through) dalam switch
pernyataan. Saat pengguna mengklik tautan judul kolom, nilai yang sesuai sortOrder
disediakan dalam nilai string kueri.
NameSort
dan DateSort
digunakan oleh Razor Halaman untuk mengonfigurasi hyperlink judul kolom dengan nilai string kueri yang sesuai:
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();
}
Kode berikut berisi operator C# kondisi? :
NameSort = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
DateSort = sortOrder == "Date" ? "date_desc" : "Date";
Baris pertama menentukan bahwa kapan sortOrder
null atau kosong, NameSort
diatur ke "name_desc." Jika sortOrder
tidak null atau kosong, NameSort
diatur ke string kosong.
ini ?: operator
juga dikenal sebagai operator ternary.
Kedua pernyataan ini memungkinkan halaman untuk mengatur hyperlink judul kolom sebagai berikut:
Urutan pengurutan saat ini | Hyperlink Nama Belakang | Hyperlink Tanggal |
---|---|---|
Nama Belakang naik | Urut turun | urutan naik |
Nama Belakang turun | urutan naik | urutan naik |
Tanggal naik | urutan naik | Urut turun |
Tanggal turun | urutan naik | urutan naik |
Metode ini menggunakan LINQ ke Entitas untuk menentukan kolom yang akan diurutkan. Kode menginisialisasi IQueryable<Student>
sebelum pernyataan pengalihan, dan memodifikasinya dalam pernyataan pengalihan:
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
Saat dibuat atau dimodifikasi, tidak ada kueri yang dikirim ke database. Kueri tidak dijalankan sampai objek dikonversi IQueryable
menjadi koleksi. IQueryable
dikonversi ke koleksi dengan memanggil metode seperti ToListAsync
. Oleh karena itu, IQueryable
kode menghasilkan satu kueri yang tidak dijalankan hingga pernyataan berikut:
Student = await studentIQ.AsNoTracking().ToListAsync();
OnGetAsync
bisa mendapatkan verbose dengan sejumlah besar kolom yang dapat diurutkan.
Menambahkan hyperlink judul kolom ke halaman Indeks Siswa
Ganti kode di Students/Index.cshtml
, dengan kode yang disorot berikut:
@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>
Kode sebelumnya:
- Menambahkan hyperlink ke
LastName
judul kolom danEnrollmentDate
. - Menggunakan informasi dalam
NameSort
danDateSort
untuk menyiapkan hyperlink dengan nilai susunan urutan saat ini.
Untuk memverifikasi bahwa pengurutan berfungsi:
- Jalankan aplikasi dan pilih tab Siswa .
- Klik Nama Belakang.
- Klik Tanggal Pendaftaran.
Untuk mendapatkan pemahaman yang lebih baik tentang kode:
- Di
Students/Index.cshtml.cs
, atur titik henti padaswitch (sortOrder)
. - Tambahkan jam tangan untuk
NameSort
danDateSort
. - Di
Students/Index.cshtml
, atur titik henti pada@Html.DisplayNameFor(model => model.Student[0].LastName)
.
Menelusuri debugger.
Menambahkan Kotak Pencarian ke halaman Indeks Siswa
Untuk menambahkan pemfilteran ke halaman Indeks Siswa:
- Kotak teks dan tombol kirim ditambahkan ke Razor Halaman. Kotak teks menyediakan string pencarian pada nama depan atau belakang.
- Model halaman diperbarui untuk menggunakan nilai kotak teks.
Menambahkan fungsionalitas pemfilteran ke metode Indeks
Students/Index.cshtml.cs
OnGetAsync
Perbarui dengan kode berikut:
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();
}
Kode sebelumnya:
searchString
Menambahkan parameter keOnGetAsync
metode . Nilai string pencarian diterima dari kotak teks yang ditambahkan di bagian berikutnya.- Ditambahkan ke klausa pernyataan
Where
LINQ. KlausaWhere
hanya memilih siswa yang nama depan atau nama belakangnya berisi string pencarian. Pernyataan LINQ dijalankan hanya jika ada nilai untuk dicari.
Catatan: Kode sebelumnya memanggil metode pada IQueryable
objek, dan filter diproses Where
di server. Dalam beberapa skenario, aplikasi mungkin memanggil Where
metode sebagai metode ekstensi pada koleksi dalam memori. Misalnya, misalkan _context.Students
perubahan dari EF CoreDbSet
ke metode repositori yang mengembalikan IEnumerable
koleksi. Hasilnya biasanya akan sama tetapi dalam beberapa kasus mungkin berbeda.
Misalnya, implementasi .NET Framework melakukan perbandingan Contains
peka huruf besar/kecil secara default. Di SQL Server, Contains
sensitivitas huruf besar/kecil ditentukan oleh pengaturan kolase instans SQL Server. SQL Server default menjadi tidak peka huruf besar/kecil. ToUpper
dapat dipanggil untuk membuat pengujian secara eksplisit tidak peka huruf besar/kecil:
Where(s => s.LastName.ToUpper().Contains(searchString.ToUpper())
Kode sebelumnya akan memastikan bahwa hasilnya tidak peka huruf besar/kecil jika kode berubah untuk menggunakan IEnumerable
. Ketika Contains
dipanggil pada IEnumerable
koleksi, implementasi .NET Core digunakan. Ketika Contains
dipanggil pada IQueryable
objek, implementasi database digunakan. Mengembalikan IEnumerable
dari repositori dapat memiliki penalti performa yang signifikan:
- Semua baris dikembalikan dari server DB.
- Filter diterapkan ke semua baris yang dikembalikan dalam aplikasi.
Ada penalti performa untuk memanggil ToUpper
. Kode ToUpper
menambahkan fungsi dalam klausa WHERE dari pernyataan TSQL SELECT. Fungsi yang ditambahkan mencegah pengoptimal menggunakan indeks. Mengingat bahwa SQL diinstal sebagai tidak peka huruf besar/kecil, sebaiknya hindari ToUpper
panggilan saat tidak diperlukan.
Menambahkan Kotak Pencarian ke halaman Indeks Siswa
Di Pages/Students/Index.cshtml
, tambahkan kode yang disorot berikut untuk membuat tombol Pencarian dan krom bermakna.
@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">
Kode sebelumnya menggunakan pembantu <form>
tag untuk menambahkan kotak teks pencarian dan tombol. Secara default, pembantu <form>
tag mengirimkan data formulir dengan POST. Dengan POST, parameter diteruskan dalam isi pesan HTTP dan bukan di URL. Saat HTTP GET digunakan, data formulir diteruskan di URL sebagai string kueri. Meneruskan data dengan string kueri memungkinkan pengguna untuk menandai URL. Panduan W3C merekomendasikan bahwa GET harus digunakan saat tindakan tidak menghasilkan pembaruan.
Uji aplikasi:
- Pilih tab Siswa dan masukkan string pencarian.
- Pilih Telusuri.
Perhatikan bahwa URL berisi string pencarian.
http://localhost:5000/Students?SearchString=an
Jika halaman diberi marka buku, marka buku berisi URL ke halaman dan SearchString
string kueri. Dalam method="get"
form
tag adalah apa yang menyebabkan string kueri dihasilkan.
Saat ini, saat tautan pengurutan judul kolom dipilih, nilai filter dari kotak Pencarian hilang. Nilai filter yang hilang diperbaiki di bagian berikutnya.
Menambahkan fungsionalitas halaman ke halaman Indeks Siswa
Di bagian ini, PaginatedList
kelas dibuat untuk mendukung halaman. Kelas PaginatedList
menggunakan Skip
pernyataan dan Take
untuk memfilter data di server alih-alih mengambil semua baris tabel. Ilustrasi berikut menunjukkan tombol halaman.
Di folder proyek, buat PaginatedList.cs
dengan kode berikut:
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);
}
}
}
Metode CreateAsync
dalam kode sebelumnya mengambil ukuran halaman dan nomor halaman dan menerapkan pernyataan dan Take
yang sesuai Skip
ke IQueryable
. Ketika ToListAsync
dipanggil pada IQueryable
, itu mengembalikan Daftar yang hanya berisi halaman yang diminta. Properti HasPreviousPage
dan HasNextPage
digunakan untuk mengaktifkan atau menonaktifkan tombol halaman Sebelumnya dan Berikutnya .
Metode CreateAsync
ini digunakan untuk membuat PaginatedList<T>
. Konstruktor tidak dapat membuat PaginatedList<T>
objek, konstruktor tidak dapat menjalankan kode asinkron.
Menambahkan fungsionalitas halaman ke metode Indeks
Di Students/Index.cshtml.cs
, perbarui jenis dari Student
IList<Student>
ke PaginatedList<Student>
:
public PaginatedList<Student> Student { get; set; }
Students/Index.cshtml.cs
OnGetAsync
Perbarui dengan kode berikut:
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);
}
Kode sebelumnya menambahkan indeks halaman, saat ini sortOrder
, dan ke currentFilter
tanda tangan metode.
public async Task OnGetAsync(string sortOrder,
string currentFilter, string searchString, int? pageIndex)
Semua parameter null ketika:
- Halaman dipanggil dari tautan Siswa .
- Pengguna belum mengklik tautan penomoran atau pengurutan.
Saat tautan halaman diklik, variabel indeks halaman berisi nomor halaman yang akan ditampilkan.
CurrentSort
Razor menyediakan Halaman dengan urutan pengurutan saat ini. Urutan pengurutan saat ini harus disertakan dalam tautan halaman untuk mempertahankan urutan pengurutan saat penomoran halaman.
CurrentFilter
Razor menyediakan Halaman dengan string filter saat ini. Nilai CurrentFilter
:
- Harus disertakan dalam tautan halaman untuk mempertahankan pengaturan filter selama halaman.
- Harus dipulihkan ke kotak teks ketika halaman diputar ulang.
Jika string pencarian diubah saat halaman, halaman diatur ulang ke 1. Halaman harus direset ke 1 karena filter baru dapat menghasilkan data yang berbeda untuk ditampilkan. Saat nilai pencarian dimasukkan dan Kirim dipilih:
- String pencarian diubah.
- Parameter
searchString
tidak null.
if (searchString != null)
{
pageIndex = 1;
}
else
{
searchString = currentFilter;
}
Metode ini PaginatedList.CreateAsync
mengonversi kueri siswa menjadi satu halaman siswa dalam jenis koleksi yang mendukung halaman. Halaman tunggal siswa tersebut diteruskan ke Razor Halaman.
Student = await PaginatedList<Student>.CreateAsync(
studentIQ.AsNoTracking(), pageIndex ?? 1, pageSize);
Dua tanda tanya mewakili PaginatedList.CreateAsync
operator null-coalescing. Operator null-coalescing mendefinisikan nilai default untuk jenis nullable. Ekspresi (pageIndex ?? 1)
berarti mengembalikan nilai pageIndex
jika memiliki nilai. Jika pageIndex
tidak memiliki nilai, kembalikan 1.
Menambahkan tautan halaman ke Halaman siswa Razor
Perbarui markup di Students/Index.cshtml
. Perubahan disorot:
@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>
Tautan header kolom menggunakan string kueri untuk meneruskan string pencarian saat ini ke OnGetAsync
metode sehingga pengguna dapat mengurutkan dalam hasil filter:
<a asp-page="./Index" asp-route-sortOrder="@Model.NameSort"
asp-route-currentFilter="@Model.CurrentFilter">
@Html.DisplayNameFor(model => model.Student[0].LastName)
</a>
Tombol halaman ditampilkan oleh pembantu tag:
<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>
Jalankan aplikasi dan navigasikan ke halaman siswa.
- Untuk memastikan penomoran berfungsi, klik tautan halaman dalam urutan pengurutan yang berbeda.
- Untuk memverifikasi bahwa penomoran berfungsi dengan benar dengan pengurutan dan pemfilteran, masukkan string pencarian dan coba penomoran halaman.
Untuk mendapatkan pemahaman yang lebih baik tentang kode:
- Di
Students/Index.cshtml.cs
, atur titik henti padaswitch (sortOrder)
. - Tambahkan jam tangan untuk
NameSort
,DateSort
,CurrentSort
, danModel.Student.PageIndex
. - Di
Students/Index.cshtml
, atur titik henti pada@Html.DisplayNameFor(model => model.Student[0].LastName)
.
Menelusuri debugger.
Perbarui halaman Tentang untuk menampilkan statistik siswa
Dalam langkah ini, Pages/About.cshtml
diperbarui untuk menampilkan berapa banyak siswa yang telah mendaftar untuk setiap tanggal pendaftaran. Pembaruan menggunakan pengelompokan dan menyertakan langkah-langkah berikut:
- Buat model tampilan untuk data yang digunakan oleh Halaman Tentang .
- Perbarui halaman Tentang untuk menggunakan model tampilan.
Membuat model tampilan
Buat folder SchoolViewModels di folder Model .
Di folder SchoolViewModels, tambahkan EnrollmentDateGroup.cs
dengan kode berikut:
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; }
}
}
Memperbarui model halaman Tentang
Templat web di ASP.NET Core 2.2 tidak menyertakan halaman Tentang. Jika Anda menggunakan ASP.NET Core 2.2, buat Halaman Tentang Razor .
Pages/About.cshtml.cs
Perbarui file dengan kode berikut:
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();
}
}
}
Pernyataan LINQ mengelompokkan entitas siswa berdasarkan tanggal pendaftaran, menghitung jumlah entitas di setiap grup, dan menyimpan hasilnya dalam kumpulan EnrollmentDateGroup
objek model tampilan.
Ubah Halaman Tentang Razor
Ganti kode dalam Pages/About.cshtml
file dengan kode berikut:
@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>
Jalankan aplikasi dan navigasikan ke halaman Tentang. Jumlah siswa untuk setiap tanggal pendaftaran ditampilkan dalam tabel.
Jika Anda mengalami masalah yang tidak dapat Anda selesaikan, unduh aplikasi yang telah selesai untuk tahap ini.
Sumber Daya Tambahan:
Dalam tutorial berikutnya, aplikasi menggunakan migrasi untuk memperbarui model data.
ASP.NET Core
Saran dan Komentar
https://aka.ms/ContentUserFeedback.
Segera hadir: Sepanjang tahun 2024 kami akan menghentikan penggunaan GitHub Issues sebagai mekanisme umpan balik untuk konten dan menggantinya dengan sistem umpan balik baru. Untuk mengetahui informasi selengkapnya, lihat:Kirim dan lihat umpan balik untuk