Tutorial: Menambahkan pengurutan, pemfilteran, dan penomoran - ASP.NET MVC dengan EF Core

Dalam tutorial sebelumnya, Anda menerapkan sekumpulan halaman web untuk operasi CRUD dasar untuk entitas Siswa. Dalam tutorial ini Anda akan menambahkan fungsi pengurutan, pemfilteran, dan penomoran halaman ke halaman Indeks Siswa. Anda juga akan membuat halaman yang melakukan pengelompokan sederhana.

Ilustrasi berikut menunjukkan seperti apa tampilan halaman saat Anda selesai. Judul kolom adalah tautan yang bisa diklik pengguna untuk mengurutkan menurut kolom tersebut. Mengklik judul kolom berulang kali beralih antara urutan urutan naik dan turun.

Students index page

Di tutorial ini, Anda akan:

  • Menambahkan tautan pengurutan kolom
  • Tambahkan kotak Pencarian
  • Menambahkan halaman ke Indeks Siswa
  • Menambahkan halaman ke metode Indeks
  • Menambahkan tautan halaman
  • Membuat halaman Tentang

Prasyarat

Untuk menambahkan pengurutan ke halaman Indeks Siswa, Anda akan mengubah Index metode pengontrol Siswa dan menambahkan kode ke tampilan Indeks Siswa.

Menambahkan Fungsionalitas pengurutan ke metode Indeks

Di StudentsController.cs, ganti Index metode dengan kode berikut:

public async Task<IActionResult> Index(string sortOrder)
{
    ViewData["NameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
    ViewData["DateSortParm"] = sortOrder == "Date" ? "date_desc" : "Date";
    var students = from s in _context.Students
                   select s;
    switch (sortOrder)
    {
        case "name_desc":
            students = students.OrderByDescending(s => s.LastName);
            break;
        case "Date":
            students = students.OrderBy(s => s.EnrollmentDate);
            break;
        case "date_desc":
            students = students.OrderByDescending(s => s.EnrollmentDate);
            break;
        default:
            students = students.OrderBy(s => s.LastName);
            break;
    }
    return View(await students.AsNoTracking().ToListAsync());
}

Kode ini menerima sortOrder parameter dari string kueri di URL. Nilai string kueri disediakan oleh ASP.NET Core MVC sebagai parameter untuk metode tindakan. Parameter akan menjadi string yang merupakan "Nama" atau "Tanggal", secara opsional diikuti oleh garis bawah dan string "desc" untuk menentukan urutan menurut. Urutan sortir default adalah menaik.

Pertama kali halaman Indeks diminta, tidak ada string kueri. Siswa ditampilkan dalam urutan naik berdasarkan nama belakang, yang merupakan default seperti yang ditetapkan oleh kasus fall-through dalam switch pernyataan. Saat pengguna mengklik hyperlink judul kolom, nilai yang sesuai sortOrder disediakan dalam string kueri.

Dua ViewData elemen (NameSortParm dan DateSortParm) digunakan oleh tampilan untuk mengonfigurasi hyperlink judul kolom dengan nilai string kueri yang sesuai.

public async Task<IActionResult> Index(string sortOrder)
{
    ViewData["NameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
    ViewData["DateSortParm"] = sortOrder == "Date" ? "date_desc" : "Date";
    var students = from s in _context.Students
                   select s;
    switch (sortOrder)
    {
        case "name_desc":
            students = students.OrderByDescending(s => s.LastName);
            break;
        case "Date":
            students = students.OrderBy(s => s.EnrollmentDate);
            break;
        case "date_desc":
            students = students.OrderByDescending(s => s.EnrollmentDate);
            break;
        default:
            students = students.OrderBy(s => s.LastName);
            break;
    }
    return View(await students.AsNoTracking().ToListAsync());
}

Ini adalah pernyataan terner. Yang pertama menentukan bahwa jika sortOrder parameter null atau kosong, NameSortParm harus diatur ke "name_desc"; jika tidak, parameter harus diatur ke string kosong. Kedua pernyataan ini memungkinkan tampilan 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 membuat IQueryable variabel sebelum pernyataan pengalihan, memodifikasinya dalam pernyataan pengalihan, dan memanggil ToListAsync metode setelah switch pernyataan. Saat Anda membuat dan mengubah IQueryable variabel, tidak ada kueri yang dikirim ke database. Kueri tidak dijalankan sampai Anda mengonversi IQueryable objek menjadi koleksi dengan memanggil metode seperti ToListAsync. Oleh karena itu, kode ini menghasilkan satu kueri yang tidak dijalankan sampai return View pernyataan.

Kode ini bisa mendapatkan verbose dengan sejumlah besar kolom. Tutorial terakhir dalam seri ini menunjukkan cara menulis kode yang memungkinkan Anda meneruskan nama OrderBy kolom dalam variabel string.

Ganti kode di Views/Students/Index.cshtml, dengan kode berikut untuk menambahkan hyperlink judul kolom. Baris yang diubah disorot.

@model IEnumerable<ContosoUniversity.Models.Student>

@{
    ViewData["Title"] = "Index";
}

<h2>Index</h2>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
                <th>
                    <a asp-action="Index" asp-route-sortOrder="@ViewData["NameSortParm"]">@Html.DisplayNameFor(model => model.LastName)</a>
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.FirstMidName)
                </th>
                <th>
                    <a asp-action="Index" asp-route-sortOrder="@ViewData["DateSortParm"]">@Html.DisplayNameFor(model => model.EnrollmentDate)</a>
                </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model) {
        <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-action="Edit" asp-route-id="@item.ID">Edit</a> |
                <a asp-action="Details" asp-route-id="@item.ID">Details</a> |
                <a asp-action="Delete" asp-route-id="@item.ID">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

Kode ini menggunakan informasi dalam ViewData properti untuk menyiapkan hyperlink dengan nilai string kueri yang sesuai.

Jalankan aplikasi, pilih tab Siswa , dan klik judul kolom Nama Belakang dan Tanggal Pendaftaran untuk memverifikasi bahwa pengurutan berfungsi.

Students index page in name order

Untuk menambahkan pemfilteran ke halaman Indeks Siswa, Anda akan menambahkan kotak teks dan tombol kirim ke tampilan dan membuat perubahan terkait dalam Index metode . Kotak teks akan memungkinkan Anda memasukkan string untuk dicari di bidang nama depan dan nama belakang.

Menambahkan fungsionalitas pemfilteran ke metode Indeks

Di StudentsController.cs, ganti Index metode dengan kode berikut (perubahan disorot).

public async Task<IActionResult> Index(string sortOrder, string searchString)
{
    ViewData["NameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
    ViewData["DateSortParm"] = sortOrder == "Date" ? "date_desc" : "Date";
    ViewData["CurrentFilter"] = searchString;

    var students = from s in _context.Students
                   select s;
    if (!String.IsNullOrEmpty(searchString))
    {
        students = students.Where(s => s.LastName.Contains(searchString)
                               || s.FirstMidName.Contains(searchString));
    }
    switch (sortOrder)
    {
        case "name_desc":
            students = students.OrderByDescending(s => s.LastName);
            break;
        case "Date":
            students = students.OrderBy(s => s.EnrollmentDate);
            break;
        case "date_desc":
            students = students.OrderByDescending(s => s.EnrollmentDate);
            break;
        default:
            students = students.OrderBy(s => s.LastName);
            break;
    }
    return View(await students.AsNoTracking().ToListAsync());
}

Anda telah menambahkan searchString parameter ke Index metode . Nilai string pencarian diterima dari kotak teks yang akan Anda tambahkan ke tampilan Indeks. Anda juga telah menambahkan ke pernyataan LINQ di mana klausa yang hanya memilih siswa yang nama depan atau nama belakangnya berisi string pencarian. Pernyataan yang menambahkan klausul di mana dijalankan hanya jika ada nilai untuk dicari.

Catatan

Di sini Anda memanggil Where metode pada IQueryable objek, dan filter akan diproses di server. Dalam beberapa skenario, Anda mungkin memanggil Where metode sebagai metode ekstensi pada koleksi dalam memori. (Misalnya, Anda mengubah referensi menjadi _context.Students sehingga alih-alih EF DbSet mereferensikan metode repositori yang mengembalikan IEnumerable koleksi.) Hasilnya biasanya akan sama tetapi dalam beberapa kasus mungkin berbeda.

Misalnya, implementasi .NET Framework dari Contains metode melakukan perbandingan peka huruf besar/kecil secara default, tetapi di SQL Server ini ditentukan oleh pengaturan kolase instans SQL Server. Pengaturan tersebut default ke tidak peka huruf besar/kecil. Anda dapat memanggil ToUpper metode untuk membuat pengujian secara eksplisit tidak peka huruf besar/kecil: Where(s => s.LastName.ToUpper(). Contains(searchString.ToUpper()). Itu akan memastikan bahwa hasilnya tetap sama jika Anda mengubah kode nanti untuk menggunakan repositori yang mengembalikan IEnumerable koleksi alih-alih IQueryable objek. (Ketika Anda memanggil Contains metode pada IEnumerable koleksi, Anda mendapatkan implementasi .NET Framework; ketika Anda memanggilnya pada IQueryable objek, Anda mendapatkan implementasi penyedia database.) Namun, ada penalti performa untuk solusi ini. Kode ToUpper akan menempatkan fungsi dalam klausul WHERE dari pernyataan TSQL SELECT. Itu akan mencegah pengoptimal menggunakan indeks. Mengingat bahwa SQL sebagian besar diinstal sebagai tidak peka huruf besar/kecil, yang terbaik adalah menghindari ToUpper kode sampai Anda bermigrasi ke penyimpanan data peka huruf besar/kecil.

Menambahkan Kotak Pencarian ke Tampilan Indeks Siswa

Di Views/Student/Index.cshtml, tambahkan kode yang disorot segera sebelum tag tabel pembuka untuk membuat keterangan, kotak teks, dan tombol Cari .

<p>
    <a asp-action="Create">Create New</a>
</p>

<form asp-action="Index" method="get">
    <div class="form-actions no-color">
        <p>
            Find by name: <input type="text" name="SearchString" value="@ViewData["CurrentFilter"]" />
            <input type="submit" value="Search" class="btn btn-default" /> |
            <a asp-action="Index">Back to Full List</a>
        </p>
    </div>
</form>

<table class="table">

Kode ini menggunakan pembantu <form> tag untuk menambahkan kotak teks pencarian dan tombol. Secara default, pembantu <form> tag mengirimkan data formulir dengan POST, yang berarti bahwa parameter diteruskan dalam isi pesan HTTP dan bukan di URL sebagai string kueri. Saat Anda menentukan HTTP GET, data formulir diteruskan dalam URL sebagai string kueri, yang memungkinkan pengguna untuk menandai URL. Panduan W3C menyarankan agar Anda menggunakan GET saat tindakan tidak menghasilkan pembaruan.

Jalankan aplikasi, pilih tab Siswa , masukkan string pencarian, dan klik Cari untuk memverifikasi bahwa pemfilteran berfungsi.

Students index page with filtering

Perhatikan bahwa URL berisi string pencarian.

http://localhost:5813/Students?SearchString=an

Jika Anda menandai halaman ini, Anda akan mendapatkan daftar yang difilter saat menggunakan marka buku. method="get" Menambahkan ke form tag adalah apa yang menyebabkan string kueri dihasilkan.

Pada tahap ini, jika Anda mengklik tautan pengurutan judul kolom, Anda akan kehilangan nilai filter yang Anda masukkan di kotak Pencarian . Anda akan memperbaikinya di bagian berikutnya.

Menambahkan halaman ke Indeks Siswa

Untuk menambahkan halaman halaman Indeks Siswa, Anda akan membuat PaginatedList kelas yang menggunakan Skip pernyataan dan Take untuk memfilter data di server alih-alih selalu mengambil semua baris tabel. Kemudian Anda akan membuat perubahan tambahan dalam Index metode dan menambahkan tombol halaman ke Index tampilan. Ilustrasi berikut menunjukkan tombol halaman.

Students index page with paging links

Di folder proyek, buat PaginatedList.cs, lalu ganti kode templat 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 ini mengambil ukuran halaman dan nomor halaman dan menerapkan pernyataan dan Take yang sesuai Skip ke IQueryable. Ketika ToListAsync dipanggil pada IQueryable, itu akan mengembalikan Daftar yang hanya berisi halaman yang diminta. Properti HasPreviousPage dan HasNextPage dapat digunakan untuk mengaktifkan atau menonaktifkan tombol halaman Sebelumnya dan Berikutnya .

Metode CreateAsync digunakan alih-alih konstruktor untuk membuat PaginatedList<T> objek karena konstruktor tidak dapat menjalankan kode asinkron.

Menambahkan halaman ke metode Indeks

Di StudentsController.cs, ganti Index metode dengan kode berikut.

public async Task<IActionResult> Index(
    string sortOrder,
    string currentFilter,
    string searchString,
    int? pageNumber)
{
    ViewData["CurrentSort"] = sortOrder;
    ViewData["NameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
    ViewData["DateSortParm"] = sortOrder == "Date" ? "date_desc" : "Date";

    if (searchString != null)
    {
        pageNumber = 1;
    }
    else
    {
        searchString = currentFilter;
    }

    ViewData["CurrentFilter"] = searchString;

    var students = from s in _context.Students
                   select s;
    if (!String.IsNullOrEmpty(searchString))
    {
        students = students.Where(s => s.LastName.Contains(searchString)
                               || s.FirstMidName.Contains(searchString));
    }
    switch (sortOrder)
    {
        case "name_desc":
            students = students.OrderByDescending(s => s.LastName);
            break;
        case "Date":
            students = students.OrderBy(s => s.EnrollmentDate);
            break;
        case "date_desc":
            students = students.OrderByDescending(s => s.EnrollmentDate);
            break;
        default:
            students = students.OrderBy(s => s.LastName);
            break;
    }

    int pageSize = 3;
    return View(await PaginatedList<Student>.CreateAsync(students.AsNoTracking(), pageNumber ?? 1, pageSize));
}

Kode ini menambahkan parameter nomor halaman, parameter urutan pengurutan saat ini, dan parameter filter saat ini ke tanda tangan metode.

public async Task<IActionResult> Index(
    string sortOrder,
    string currentFilter,
    string searchString,
    int? pageNumber)

Pertama kali halaman ditampilkan, atau jika pengguna belum mengklik tautan penomoran halaman atau pengurutan, semua parameter akan null. Jika tautan halaman diklik, variabel halaman akan berisi nomor halaman yang akan ditampilkan.

Elemen ViewData bernama CurrentSort menyediakan tampilan dengan urutan pengurutan saat ini, karena ini harus disertakan dalam tautan halaman agar urutan pengurutan tetap sama saat penomoran halaman.

ViewData Elemen bernama CurrentFilter menyediakan tampilan dengan string filter saat ini. Nilai ini harus disertakan dalam tautan halaman untuk mempertahankan pengaturan filter selama halaman, dan harus dipulihkan ke kotak teks saat halaman diputar ulang.

Jika string pencarian diubah selama halaman, halaman harus direset ke 1, karena filter baru dapat menghasilkan data yang berbeda untuk ditampilkan. String pencarian diubah saat nilai dimasukkan dalam kotak teks dan tombol Kirim ditekan. Dalam hal ini, searchString parameter tidak null.

if (searchString != null)
{
    pageNumber = 1;
}
else
{
    searchString = currentFilter;
}

Di akhir Index metode, metode mengonversi PaginatedList.CreateAsync kueri siswa menjadi satu halaman siswa dalam jenis koleksi yang mendukung penomor. Halaman tunggal siswa tersebut kemudian diteruskan ke tampilan.

return View(await PaginatedList<Student>.CreateAsync(students.AsNoTracking(), pageNumber ?? 1, pageSize));

Metode ini PaginatedList.CreateAsync mengambil nomor halaman. Dua tanda tanya mewakili operator null-coalescing. Operator null-coalescing mendefinisikan nilai default untuk jenis nullable; ekspresi (pageNumber ?? 1) berarti mengembalikan nilai pageNumber jika memiliki nilai, atau mengembalikan 1 jika pageNumber null.

Di Views/Students/Index.cshtml, ganti kode yang ada dengan kode berikut. Perubahan disorot.

@model PaginatedList<ContosoUniversity.Models.Student>

@{
    ViewData["Title"] = "Index";
}

<h2>Index</h2>

<p>
    <a asp-action="Create">Create New</a>
</p>

<form asp-action="Index" method="get">
    <div class="form-actions no-color">
        <p>
            Find by name: <input type="text" name="SearchString" value="@ViewData["CurrentFilter"]" />
            <input type="submit" value="Search" class="btn btn-default" /> |
            <a asp-action="Index">Back to Full List</a>
        </p>
    </div>
</form>

<table class="table">
    <thead>
        <tr>
            <th>
                <a asp-action="Index" asp-route-sortOrder="@ViewData["NameSortParm"]" asp-route-currentFilter="@ViewData["CurrentFilter"]">Last Name</a>
            </th>
            <th>
                First Name
            </th>
            <th>
                <a asp-action="Index" asp-route-sortOrder="@ViewData["DateSortParm"]" asp-route-currentFilter="@ViewData["CurrentFilter"]">Enrollment Date</a>
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model)
        {
            <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-action="Edit" asp-route-id="@item.ID">Edit</a> |
                    <a asp-action="Details" asp-route-id="@item.ID">Details</a> |
                    <a asp-action="Delete" asp-route-id="@item.ID">Delete</a>
                </td>
            </tr>
        }
    </tbody>
</table>

@{
    var prevDisabled = !Model.HasPreviousPage ? "disabled" : "";
    var nextDisabled = !Model.HasNextPage ? "disabled" : "";
}

<a asp-action="Index"
   asp-route-sortOrder="@ViewData["CurrentSort"]"
   asp-route-pageNumber="@(Model.PageIndex - 1)"
   asp-route-currentFilter="@ViewData["CurrentFilter"]"
   class="btn btn-default @prevDisabled">
    Previous
</a>
<a asp-action="Index"
   asp-route-sortOrder="@ViewData["CurrentSort"]"
   asp-route-pageNumber="@(Model.PageIndex + 1)"
   asp-route-currentFilter="@ViewData["CurrentFilter"]"
   class="btn btn-default @nextDisabled">
    Next
</a>

Pernyataan @model di bagian atas halaman menentukan bahwa tampilan sekarang mendapatkan PaginatedList<T> objek alih-alih List<T> objek.

Tautan header kolom menggunakan string kueri untuk meneruskan string pencarian saat ini ke pengontrol sehingga pengguna dapat mengurutkan dalam hasil filter:

<a asp-action="Index" asp-route-sortOrder="@ViewData["DateSortParm"]" asp-route-currentFilter ="@ViewData["CurrentFilter"]">Enrollment Date</a>

Tombol halaman ditampilkan oleh pembantu tag:

<a asp-action="Index"
   asp-route-sortOrder="@ViewData["CurrentSort"]"
   asp-route-pageNumber="@(Model.PageIndex - 1)"
   asp-route-currentFilter="@ViewData["CurrentFilter"]"
   class="btn btn-default @prevDisabled">
   Previous
</a>

Jalankan aplikasi dan buka halaman Siswa.

Students index page with paging links

Klik tautan halaman dalam urutan pengurutan yang berbeda untuk memastikan penomoran halaman berfungsi. Kemudian masukkan string pencarian dan coba halaman lagi untuk memverifikasi bahwa penomoran halaman juga berfungsi dengan benar dengan pengurutan dan pemfilteran.

Membuat halaman Tentang

Untuk halaman Tentang situs web Contoso University, Anda akan menampilkan berapa banyak siswa yang telah mendaftar untuk setiap tanggal pendaftaran. Ini memerlukan pengelompokan dan perhitungan sederhana pada grup. Untuk mencapai hal ini, Anda akan melakukan hal berikut:

  • Buat kelas model tampilan untuk data yang perlu Anda teruskan ke tampilan.
  • Buat metode Tentang di Home pengontrol.
  • Buat tampilan Tentang.

Membuat model tampilan

Buat folder SchoolViewModels di folder Model .

Di folder baru, tambahkan file EnrollmentDateGroup.cs kelas dan ganti kode templat 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; }
    }
}

Home Mengubah Pengontrol

Di HomeController.cs, tambahkan pernyataan penggunaan berikut di bagian atas file:

using Microsoft.EntityFrameworkCore;
using ContosoUniversity.Data;
using ContosoUniversity.Models.SchoolViewModels;
using Microsoft.Extensions.Logging;

Tambahkan variabel kelas untuk konteks database segera setelah kurung kurawal pembuka untuk kelas , dan dapatkan instans konteks dari ASP.NET Core DI:

public class HomeController : Controller
{
    private readonly ILogger<HomeController> _logger;
    private readonly SchoolContext _context;

    public HomeController(ILogger<HomeController> logger, SchoolContext context)
    {
        _logger = logger;
        _context = context;
    }

About Tambahkan metode dengan kode berikut:

public async Task<ActionResult> About()
{
    IQueryable<EnrollmentDateGroup> data = 
        from student in _context.Students
        group student by student.EnrollmentDate into dateGroup
        select new EnrollmentDateGroup()
        {
            EnrollmentDate = dateGroup.Key,
            StudentCount = dateGroup.Count()
        };
    return View(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.

Membuat Tampilan Tentang

Views/Home/About.cshtml Tambahkan file dengan kode berikut:

@model IEnumerable<ContosoUniversity.Models.SchoolViewModels.EnrollmentDateGroup>

@{
    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)
    {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.EnrollmentDate)
            </td>
            <td>
                @item.StudentCount
            </td>
        </tr>
    }
</table>

Jalankan aplikasi dan buka halaman Tentang. Jumlah siswa untuk setiap tanggal pendaftaran ditampilkan dalam tabel.

Mendapatkan kode

Unduh atau lihat aplikasi yang telah selesai.

Langkah berikutnya

Di tutorial ini, Anda akan:

  • Menambahkan tautan pengurutan kolom
  • Menambahkan kotak Pencarian
  • Menambahkan halaman ke Indeks Siswa
  • Menambahkan halaman ke metode Indeks
  • Menambahkan tautan halaman
  • Membuat halaman Tentang

Lanjutkan ke tutorial berikutnya untuk mempelajari cara menangani perubahan model data dengan menggunakan migrasi.