ASP.NET Core 中的 Razor 頁面和 EF Core - 排序、篩選、分頁

2019-04-17 08:57 更新

Contoso University Web 應(yīng)用演示了如何使用 EF Core 和 Visual Studio 創(chuàng)建 Razor 頁面 Web 應(yīng)用。若要了解系列教程,請參閱第一個教程

本教程將添加排序、篩選、分組和分頁功能。

下圖顯示完整的頁面。 列標題是可單擊的鏈接,可用于對列進行排序。 重復(fù)單擊列標題可在升降和降序排序順序之間切換。

“學(xué)生索引”頁

如果遇到無法解決的問題,請下載已完成應(yīng)用。

向索引頁添加排序

向 Students/Index.cshtml.cs PageModel 添加字符串,使其包含排序參數(shù):

C#

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:

C#

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

上述代碼接收來自 URL 中的查詢字符串的 sortOrder 參數(shù)。 該 URL(包括查詢字符串)由定位點標記幫助器生成

sortOrder 參數(shù)為“名稱”或“日期”。 sortOrder 參數(shù)后面可跟“_desc”以指定降序(可選)。 默認排序順序為升序。

如果通過“學(xué)生”鏈接對“索引”頁發(fā)起請求,則不會有任何查詢字符串。 學(xué)生按姓氏升序顯示。 按姓氏升序是 switch 語句中的默認順序 (fall-through case)。 用戶單擊列標題鏈接時,查詢字符串值中會提供相應(yīng)的 sortOrder 值。

Razor 頁面使用 NameSort 和 DateSort 為列標題超鏈接配置相應(yīng)的查詢字符串值:

C#

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

以下代碼包含 C# 條件 ?: 運算符

C#

NameSort = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
DateSort = sortOrder == "Date" ? "date_desc" : "Date";

第一行指定當 sortOrder 為 NULL 或為空時,NameSort 設(shè)置為“name_desc”。 如果 sortOrder 不為 NULL 或不為空,則 NameSort 設(shè)置為空字符串。

?: operator 也稱為三元運算符。

通過這兩個語句,頁面可如下設(shè)置列標題超鏈接:

當前排序順序姓氏超鏈接日期超鏈接
姓氏升序descendingascending
姓氏降序ascendingascending
日期升序ascendingdescending
日期降序ascendingascending

該方法使用 LINQ to Entities 指定要作為排序依據(jù)的列。 此代碼會初始化 switch 語句前面的 IQueryable<Student>,并在 switch 語句中對其進行修改:

C#

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

創(chuàng)建或修改 IQueryable 時,不會向數(shù)據(jù)庫發(fā)送任何查詢。 將 IQueryable 對象轉(zhuǎn)換成集合后才能執(zhí)行查詢。 通過調(diào)用 IQueryable 等方法可將 ToListAsync 轉(zhuǎn)換成集合。 因此,IQueryable 代碼會生成單個查詢,此查詢直到出現(xiàn)以下語句才執(zhí)行:

C#

Student = await studentIQ.AsNoTracking().ToListAsync();

OnGetAsync 可能獲得包含大量可排序列的詳細信息。

向“學(xué)生索引”頁添加列標題超鏈接

將 Students/Index.cshtml 中的代碼替換為以下突出顯示的代碼:

HTML

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

前面的代碼:

  • 向 LastName 和 EnrollmentDate 列標題添加超鏈接。
  • 使用 NameSort 和 DateSort 中的信息為超鏈接設(shè)置當前的排序順序值。

若要驗證排序是否生效:

  • 運行應(yīng)用并選擇“學(xué)生”選項卡。
  • 單擊“姓氏”。
  • 單擊“注冊日期”。

若要更好地了解此代碼:

  • 請在 Student/Index.cshtml.cs 中的 switch (sortOrder) 上設(shè)置斷點。
  • 添加對 NameSort 和 DateSort 的監(jiān)視。
  • 請在 Student/Index.cshtml.cs 中的 @Html.DisplayNameFor(model => model.Student[0].LastName) 上設(shè)置斷點。

單步執(zhí)行調(diào)試程序。

向“學(xué)生索引”頁添加搜索框

若要向“學(xué)生索引”頁添加篩選:

  • 需要向 Razor 頁面添加一個文本框和一個提交按鈕。 文本框會針對名字或姓氏提供一個搜索字符串。
  • 頁面模型隨即更新以使用文本框值。

向 Index 方法添加篩選功能

用以下代碼更新 Students/Index.cshtml.cs OnGetAsync:

C#

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

前面的代碼:

  • 向 OnGetAsync 方法添加 searchString 參數(shù)。 從下一部分中添加的文本框中所接收搜索字符串值。
  • 已向 LINQ 語句添加 Where 子句。 Where 子句僅選擇其名字或姓氏中包含搜索字符串的學(xué)生。 只有存在要搜索的值時才執(zhí)行 LINQ 語句。

注意:上述代碼調(diào)用 IQueryable 對象上的 Where 方法,且在服務(wù)器上處理該篩選器。 在某些情況下,應(yīng)用可能會對內(nèi)存中的集合調(diào)用 Where 方法作為擴展方法。 例如,假設(shè) _context.Students 從 EF Core DbSet 更改為可返回 IEnumerable 集合的存儲庫方法。 結(jié)果通常是相同的,但在某些情況下可能不同。

例如,Contains 的 .NET Framework 實現(xiàn)會默認執(zhí)行區(qū)分大小寫的比較。 在 SQL Server 中,Contains 區(qū)分大小寫由 SQL Server 實例的排序規(guī)則設(shè)置決定。 SQL Server 默認為不區(qū)分大小寫??烧{(diào)用 ToUpper,進行不區(qū)分大小寫的顯式測試:

Where(s => s.LastName.ToUpper().Contains(searchString.ToUpper())

如果上述代碼改為使用 IEnumerable,則該代碼會確保結(jié)果區(qū)分大小寫。 如果在 IEnumerable 集合上調(diào)用 Contains,則使用 .NET Core 實現(xiàn)。 如果在 IQueryable 對象上調(diào)用 Contains,則使用數(shù)據(jù)庫實現(xiàn)。 從存儲庫返回 IEnumerable 可能會大幅降低性能:

  1. 所有行均從 DB 服務(wù)器返回。
  2. 篩選應(yīng)用于應(yīng)用程序中所有返回的行。

調(diào)用 ToUpper 不會對性能產(chǎn)生負面影響。 ToUpper 代碼會在 TSQL SELECT 語句的 WHERE 子句中添加一個函數(shù)。 添加的函數(shù)會防止優(yōu)化器使用索引。 如果安裝的 SQL 區(qū)分大小寫,則最好避免在不必要時調(diào)用 ToUpper。

向“學(xué)生索引”頁添加搜索框

在 Pages/Students/Index.cshtml中,添加以下突出顯示的代碼以創(chuàng)建“搜索”按鈕和各種 chrome。

HTML

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

上述代碼使用 <form> 標記幫助器來添加搜索文本框和按鈕。 默認情況下,<form> 標記幫助器利用 POST 提交表單數(shù)據(jù)。 借助 POST,會在 HTTP 消息正文中而不是在 URL 中傳遞參數(shù)。 使用 HTTP GET 時,表單數(shù)據(jù)作為查詢字符串在 URL 中進行傳遞。 通過查詢字符串傳遞數(shù)據(jù)時,用戶可對 URL 添加書簽。 W3C 指南建議應(yīng)在操作不引起更新的情況下使用 GET。

測試應(yīng)用:

  • 選擇“學(xué)生”選項卡并輸入搜索字符串。
  • 選擇“搜索”。

請注意,該 URL 包含搜索字符串。

HTML

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

如果頁面具有書簽,該書簽將包含該頁面的 URL 和 SearchString 查詢字符串。 form 標記中的 method="get" 會導(dǎo)致生成查詢字符串。

目前,選中列標題排序鏈接時,“搜索”框中的篩選值會丟失。 丟失的篩選值在下一部分進行修復(fù)。

向“學(xué)生索引”頁添加分頁功能

本部分將創(chuàng)建一個 PaginatedList 類來支持分頁。 PaginatedList 類使用 Skip 和 Take 語句在服務(wù)器上篩選數(shù)據(jù),而不是檢索所有表格行。 下圖顯示了分頁按鈕。

帶有分頁鏈接的“學(xué)生索引”頁

在項目文件夾中,使用以下代碼創(chuàng)建 PaginatedList.cs:

C#

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
        {
            get
            {
                return (PageIndex > 1);
            }
        }

        public bool HasNextPage
        {
            get
            {
                return (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);
        }
    }
}

上述代碼中的 CreateAsync 方法會提取頁面大小和頁碼,并將相應(yīng)的 Skip 和 Take 語句應(yīng)用于 IQueryable。 當在 IQueryable 上調(diào)用 ToListAsync 時,它將返回僅包含所請求頁的列表。 屬性 HasPreviousPage 和 HasNextPage 用于啟用或禁用“上一頁”和“下一頁”分頁按鈕。

CreateAsync 方法用于創(chuàng)建 PaginatedList<T>。 構(gòu)造函數(shù)不能創(chuàng)建 PaginatedList<T> 對象;構(gòu)造函數(shù)不能運行異步代碼。

向 Index 方法添加分頁功能

在 Students/Index.cshtml.cs 中,將 Student 的類型從 IList<Student> 更新到 PaginatedList<Student>:

C#

public PaginatedList<Student> Student { get; set; }

用以下代碼更新 Students/Index.cshtml.cs OnGetAsync:

C#

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

上述代碼會向方法簽名添加頁面索引、當前的 sortOrder 和 currentFilter。

C#

public async Task OnGetAsync(string sortOrder,
    string currentFilter, string searchString, int? pageIndex)

出現(xiàn)以下情況時,所有參數(shù)均為 NULL:

  • 從“學(xué)生”鏈接調(diào)用頁面。
  • 用戶尚未單擊分頁或排序鏈接。

單擊分頁鏈接后,頁面索引變量將包含要顯示的頁碼。

CurrentSort 為 Razor 頁面提供當前排序順序。 必須在分頁鏈接中包含當前排序順序才能在分頁時保留排序順序。

CurrentFilter 為 Razor 頁面提供當前的篩選字符串。 CurrentFilter 值:

  • 必須包含在分頁鏈接中才能在分頁過程中保留篩選設(shè)置。
  • 必須在重新顯示頁面時還原到文本框。

如果在分頁時更改搜索字符串,頁碼會重置為 1。 頁面必須重置為 1,因為新的篩選器會導(dǎo)致顯示不同的數(shù)據(jù)。 輸入搜索值并選擇“提交”時:

  • 搜索字符串將會更改。
  • searchString 參數(shù)不為 NULL。

C#

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

PaginatedList.CreateAsync 方法會將學(xué)生查詢轉(zhuǎn)換為支持分頁的集合類型中的單個學(xué)生頁面。 單個學(xué)生頁面會傳遞到 Razor 頁面。

C#

Student = await PaginatedList<Student>.CreateAsync(
    studentIQ.AsNoTracking(), pageIndex ?? 1, pageSize);

PaginatedList.CreateAsync 中的兩個問號表示 NULL 合并運算符。 NULL 合并運算符定義可為 NULL 的類型的默認值。 (pageIndex ?? 1) 表達式表示返回 pageIndex 的值(若帶有值)。 如果 pageIndex 沒有值,則返回 1。

向“學(xué)生”Razor 頁面添加分頁鏈接

更新 Students/Index.cshtml 中的標記。 突出顯示所作更改:

HTML

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

列標題鏈接使用查詢字符串將當前搜索字符串傳遞到 OnGetAsync 方法,讓用戶可對篩選結(jié)果進行排序:

HTML

<a asp-page="./Index" asp-route-sortOrder="@Model.NameSort"
   asp-route-currentFilter="@Model.CurrentFilter">
    @Html.DisplayNameFor(model => model.Student[0].LastName)
</a>

分頁按鈕由標記幫助器顯示:

HTML


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

運行應(yīng)用并導(dǎo)航到學(xué)生頁面。

  • 為確保分頁生效,請單擊不同排序順序的分頁鏈接。
  • 要驗證確保分頁后可正確地排序和篩選,請輸入搜索字符串并嘗試分頁。

帶有分頁鏈接的“學(xué)生索引”頁

若要更好地了解此代碼:

  • 請在 Student/Index.cshtml.cs 中的 switch (sortOrder) 上設(shè)置斷點。
  • 添加對 NameSort、DateSort、CurrentSort 和 Model.Student.PageIndex 的監(jiān)視。
  • 請在 Student/Index.cshtml.cs 中的 @Html.DisplayNameFor(model => model.Student[0].LastName) 上設(shè)置斷點。

單步執(zhí)行調(diào)試程序。

更新“關(guān)于”頁以顯示學(xué)生統(tǒng)計信息

此步驟將更新 Pages/About.cshtml,顯示每個注冊日期的已注冊學(xué)生的數(shù)量。 更新需使用分組并包括以下步驟:

  • 為“關(guān)于”頁使用的數(shù)據(jù)創(chuàng)建視圖模型。
  • 更新“關(guān)于”頁以使用視圖模型。

創(chuàng)建視圖模型

在 Models 文件夾中創(chuàng)建一個 SchoolViewModels 文件夾。

在 SchoolViewModels 文件夾中,使用以下代碼添加 EnrollmentDateGroup.cs:

C#

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

更新“關(guān)于”頁面模型

ASP.NET Core 2.2 中的 Web 模板不包含“關(guān)于”頁面。 如果使用的是 ASP.NET Core 2.2,請創(chuàng)建“關(guān)于 Razor”頁面。

用以下代碼更新 Pages/About.cshtml.cs 文件:

C#

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

LINQ 語句按注冊日期對學(xué)生實體進行分組,計算每組中實體的數(shù)量,并將結(jié)果存儲在 EnrollmentDateGroup 視圖模型對象的集合中。

修改“關(guān)于”Razor 頁面

將 Pages/About.cshtml 文件中的代碼替換為以下代碼:

HTML

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

運行應(yīng)用并導(dǎo)航到“關(guān)于”頁面。 表格中會顯示每個注冊日期的學(xué)生計數(shù)。

如果遇到無法解決的問題,請下載本階段的已完成應(yīng)用。

“關(guān)于”頁面



以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號