ASP.NET Core MVC 和 Entity Framework Core 入門教程 - 排序叶组、過(guò)濾、分頁(yè)历造、分組(三)

排序甩十、過(guò)濾、分頁(yè)吭产、分組

Contoso 大學(xué)示例 Web 應(yīng)用程序演示如何使用實(shí)體框架(EF)Core 2.0 和 Visual Studio 2017 創(chuàng)建 ASP.NET Core 2.0 MVC Web 應(yīng)用程序侣监。 如欲了解更多本教程相關(guān)信息,請(qǐng)參閱 入門
在前面的教程臣淤,你實(shí)現(xiàn)了一組 Student 實(shí)體的基本 CRUD 頁(yè)面橄霉。 在本節(jié)中,您將向 Student 列表頁(yè)添加排序邑蒋、 篩選和分頁(yè)功能姓蜂, 還將創(chuàng)建一個(gè)進(jìn)行簡(jiǎn)單分組的頁(yè)面。
下圖顯示本節(jié)中將會(huì)完成的頁(yè)面医吊。 用戶可以點(diǎn)擊列標(biāo)題進(jìn)行排序钱慢。 重復(fù)點(diǎn)擊列標(biāo)題將排序在升序和降序之間切換。

image.png

將列排序鏈接添加到學(xué)生索引頁(yè) (Student Index)

要在學(xué)生索引頁(yè)中添加排序卿堂,需要更改 Students 控制器中的 Index 的方法束莫,并添加代碼到 Students Index 視圖。

在 Index 方法中添加排序功能

在 StudentsController.cs草描,替換 Index 方法為以下代碼:

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

代碼從 URL 中接收 sortOrder 查詢參數(shù)览绿,此查詢參數(shù)由 ASP.NET Core MVC 提供。參數(shù)是值為 "Name" 或 "Date" 的字符串穗慕,有時(shí)候后面會(huì)帶有下劃線和字符串 "desc" 來(lái)指定降序順序饿敲。 默認(rèn)排序順序?yàn)樯颉?br> 第一次請(qǐng)求索引頁(yè)時(shí),沒(méi)有附加查詢字符串逛绵。 在默認(rèn)的 Switch default 方法中按 LastName 排序诀蓉。 當(dāng)用戶單擊列標(biāo)題栗竖,相應(yīng)的 sortOrder 將會(huì)出現(xiàn)在查詢字符串中。
兩個(gè) ViewData 元素 ( NameSortParm 和 DateSortParm )供視圖用于配置列標(biāo)題超鏈接查詢字符串渠啤。

ViewData["NameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
ViewData["DateSortParm"] = sortOrder == "Date" ? "date_desc" : "Date";
當(dāng)前排序情況 LastName 鏈接 Date 鏈接
LastName 升序 降序 升序
LastName 降序 升序 升序
Date 升序 升序 降序
Date 降序 升序 升序

這是三元選擇語(yǔ)句狐肢。 如果 sortOrder 參數(shù)為 null 或?yàn)榭眨琋ameSortParm 應(yīng)設(shè)置為 "name_desc"; 否則沥曹,設(shè)置為一個(gè)空字符串份名。 這兩個(gè)語(yǔ)句在視圖中用于設(shè)置列標(biāo)題超鏈接,如下所示:

當(dāng)前排序情況 LastName 鏈接 Date 鏈接
LastName 升序 降序 升序
LastName 降序 升序 升序
Date 升序 升序 降序
Date 降序 升序 升序

方法中使用 LINQ to Entities 指定排序列妓美。 在進(jìn)行 Switch 判斷前僵腺, 創(chuàng)建 IQueryables 變量, 在判斷之后壶栋, 調(diào)用 ToListAsync 方法辰如。 在創(chuàng)建和修改 IQueryable 變量過(guò)程中,查詢并不會(huì)真正發(fā)送到數(shù)據(jù)庫(kù)贵试,直到你通過(guò)調(diào)用一個(gè)類似 ToListAsync 的方法將 IQueryable 變量轉(zhuǎn)化為一個(gè)集合琉兜。 因此,在這段代碼中毙玻,只當(dāng)返回 View 語(yǔ)句執(zhí)行時(shí)豌蟋,查詢才真正發(fā)生。
這樣的代碼可能導(dǎo)致出現(xiàn)非常多的列變量桑滩,在本系列最后一個(gè)教程中將告訴你如何在變量中傳遞排序列名梧疲。

在學(xué)生索引視圖中添加列標(biāo)題超鏈接

為了添加列標(biāo)題超鏈接,替換 Views/Students/Index.cshtml 文件中的代碼為如下代碼:

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

代碼使用 ViewData 屬性中的信息建立超鏈接中的查詢字符串运准。
運(yùn)行應(yīng)用程序中幌氮,選擇 Student 菜單,然后單擊 Last name 和 Enrollement Date 列標(biāo)題胁澳,以驗(yàn)證排序是否生效该互。


image.png

在學(xué)生索引視圖中添加搜索框

要在學(xué)生索引頁(yè)面中添加過(guò)濾功能,您需要在視圖中添加一個(gè)文本框和一個(gè)提交按鈕听哭,并在 Index 方法中做相應(yīng)修改慢洋。 文本框中塘雳,你將輸入要在名字和姓氏字段中搜索的字符串陆盘。

在 Index 方法中添加過(guò)濾功能

在StudentsController.cs,替換 Index 方法替換為以下代碼

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

在 Index 方法中添加 searchString 參數(shù)败明,此參數(shù)值來(lái)自剛剛加入視圖中的文本框隘马。同時(shí),在 LINQ 語(yǔ)句中添加一個(gè) Where 子句來(lái)選擇名字 (first name 和 last name)中包含查詢字符串的學(xué)生妻顶。Where 子句僅當(dāng)查詢字符串中有值時(shí)才生效酸员。

備注

在這里蜒车, 您在 IQueryable 對(duì)象上調(diào)用 Where 方法, 過(guò)濾將在服務(wù)器上進(jìn)行幔嗦。某些情況下酿愧,您也可能是對(duì)內(nèi)存集合調(diào)用 Where 方法。(例如邀泉,假設(shè)你將 _context.Students 的引用嬉挡,從 EF Dataset 修改為一個(gè)返回 IEnumerable 的倉(cāng)儲(chǔ)方法。)查詢結(jié)果通常是相同的汇恤,但在某些情況下可能會(huì)有所不同庞钢。
例如,.NET Framework 實(shí)現(xiàn)的 Contains 方法默認(rèn)區(qū)分大小寫因谎。但 SQL Server 中這取決于 SQL Server 實(shí)例的排序規(guī)則設(shè)置基括,該設(shè)置默認(rèn)為不區(qū)分大小寫。 您可以調(diào)用 ToUpper 來(lái)進(jìn)行測(cè)試顯式不區(qū)分大小寫的方法:Where (s = > s.LastName.ToUpper()财岔。Contains(searchString.ToUpper())风皿。 這將確保如果稍后你修改代碼為使用返回 IEnumerable 對(duì)象的倉(cāng)儲(chǔ) Repository,而不是返回 IQueryable 對(duì)象時(shí)使鹅,結(jié)果保持相同揪阶。 (當(dāng)您在 IEnumerable 集合上調(diào)用 Contains 方法時(shí),使用的是 .NET Framework 實(shí)現(xiàn); 而在 IQueryable 對(duì)象上患朱,則使用 database provider 實(shí)現(xiàn)鲁僚。) 但是,這個(gè)解決方案將對(duì)性能產(chǎn)生負(fù)面影響裁厅。 ToUpper 代碼將在 TSQL 查詢語(yǔ)句的 Where 條件中加入函數(shù)調(diào)用冰沙,進(jìn)而導(dǎo)致 SQL 優(yōu)化器停止使用索引。 假設(shè) SQL 主要安裝為不區(qū)分大小寫执虹,最好是避免 ToUpper 代碼拓挥,直到您遷移到區(qū)分大小寫的數(shù)據(jù)存儲(chǔ)區(qū)。

在 Index 視圖中添加搜索框

在Views/Student/Index.cshtml袋励,在 <Table> 標(biāo)簽前加入如下代碼侥啤,創(chuàng)建一個(gè)標(biāo)題、一個(gè)文本框和一個(gè)搜索按鈕茬故。

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

代碼使用 <form> 標(biāo)簽盖灸,添加搜索文本框和按鈕。默認(rèn)情況下磺芭,<form> 標(biāo)簽使用 POST 方法進(jìn)行數(shù)據(jù)提交赁炎,參數(shù)在消息正文而不是 URL 查詢字符串中傳遞。通過(guò)指定使用 GET 方法钾腺,窗體數(shù)據(jù)通過(guò) URL 查詢字符串進(jìn)行傳遞徙垫,這是的用戶可以對(duì) URL 創(chuàng)建書簽讥裤。 W3C 準(zhǔn)則建議,在未導(dǎo)致更新的操作中姻报,使用 GET 方法己英。
運(yùn)行應(yīng)用程序中,選擇 Student 菜單吴旋,輸入任意搜索字符剧辐,并點(diǎn)擊“搜索”按鈕,以驗(yàn)證過(guò)濾功能生效邮府。

image.png

請(qǐng)注意在 URL 中包含了搜索字符串荧关。

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

如果您將本頁(yè)面加入書簽,下次使用書簽時(shí)褂傀,您將得到過(guò)濾后的列表忍啤。在 Form 標(biāo)簽中添加的 method="get" 是產(chǎn)生查詢字符串的原因。
在此階段仙辟,如果您單擊列標(biāo)題進(jìn)行排序同波,你將丟失搜索框中輸入的過(guò)濾查詢。 在下一部分中將修復(fù)此問(wèn)題叠国。

在學(xué)生索引視圖中添加分頁(yè)功能

要在學(xué)生索引頁(yè)中添加分頁(yè)功能未檩,您將創(chuàng)建一個(gè) PaginatedList 類,在類中使用 SkipTake 語(yǔ)句實(shí)現(xiàn)在服務(wù)器過(guò)濾數(shù)據(jù)粟焊,而不是獲取數(shù)據(jù)表的所有數(shù)據(jù)行冤狡。然后再對(duì) Index 做一些更改,再 Index 視圖中添加分頁(yè)按鈕项棠。下圖中展示了分頁(yè)按鈕悲雳。

image.png

在項(xiàng)目文件夾中,創(chuàng)建 PaginatedList.cs香追,然后鍵入下面的代碼合瓢。

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è)大小及頁(yè)碼透典,再 IQueryable 對(duì)象上使用相應(yīng)的 SkipTake 語(yǔ)句晴楔。 在 IQueryable 上調(diào)用 ToListAsync 后, 返回一個(gè)只包含請(qǐng)求頁(yè)的列表峭咒。 屬性 HasPreviousPageHasNextPage 用于啟用或禁用 “上一頁(yè)” 和 “下一頁(yè)” 按鈕税弃。
PaginatedList<T> 中使用 CreateAsync 方法而不是構(gòu)造函數(shù)的原因是構(gòu)造函數(shù)無(wú)法運(yùn)行異步代碼。
ACreateAsync方法用于而不是一個(gè)構(gòu)造函數(shù)創(chuàng)建PaginatedList<T>對(duì)象讹语,因?yàn)闃?gòu)造函數(shù)不能運(yùn)行異步代碼钙皮。

Index 方法中添加分頁(yè)功能

StudentsController.cs蜂科,替換 Index 方法替換為以下代碼顽决。

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

    if (searchString != null)
    {
        page = 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(), page ?? 1, pageSize));
}

代碼在方法中添加了 page, sortOrder, currentFilter 三個(gè)參數(shù)短条。
第一次顯示頁(yè)面,或如果用戶未單擊分頁(yè)或排序鏈接才菠,則所有參數(shù)將都為 null茸时。 單擊分頁(yè)鏈接時(shí),如果頁(yè)變量將包含要顯示的頁(yè)碼赋访。
ViewData("CurrentSort") 保存當(dāng)前排序以供視圖使用可都。在視圖的分頁(yè)鏈接中包含排序,翻頁(yè)的時(shí)候才能保持排序不變蚓耽。
ViewData("CurrentFilter")保存當(dāng)前過(guò)濾字符串以供視圖使用渠牲。在視圖的分頁(yè)鏈接中包含過(guò)濾字符串,翻頁(yè)額時(shí)候才能保持過(guò)濾不變步悠。
如果在分頁(yè)期間签杈,搜索字符串被更改,因?yàn)樾碌倪^(guò)濾導(dǎo)致顯示不同的數(shù)據(jù)鼎兽,頁(yè)碼必須被重置為第一頁(yè)答姥。在文本框中輸入并按下提交按鈕時(shí),搜索字符串改變谚咬。在這種情況下鹦付,searchString 參數(shù)不為空。

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

Index 方法結(jié)尾择卦, PaginatedList.CreateAsync 方法轉(zhuǎn)化學(xué)生查詢至一個(gè)支持分頁(yè)功能的單頁(yè)學(xué)生集合敲长,然后這個(gè)集合被傳遞給視圖。

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

PaginatedList.CreateAsync 方法使用參數(shù) page (頁(yè)碼)和pageSize (頁(yè)大斜獭)作為參數(shù)潘明。 page 參數(shù)后的兩個(gè) ? 代表 null 合并運(yùn)算符null 合并運(yùn)算符 定義了可為空類型的默認(rèn)值秕噪;page ?? 1 表達(dá)式意味著钳降,如果 page 具有一個(gè)值(不為空),則返回 page腌巾, 如果為空則返回 1 遂填。

Index 視圖中添加分頁(yè)鏈接

Views/Students/Index.cshtml,替換為以下代碼澈蝙。

@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-page="@(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-page="@(Model.PageIndex + 1)"
   asp-route-currentFilter="@ViewData["CurrentFilter"]"
   class="btn btn-default @nextDisabled">
    Next
</a>

譯者注:Markdown 語(yǔ)法無(wú)法實(shí)現(xiàn)代碼內(nèi)高亮吓坚,如不清楚修改的位置,請(qǐng)參考微軟原文灯荧。
頁(yè)面頂部的 @model 指定視圖現(xiàn)在獲取 PaginatedList<T> 對(duì)象而不是 List<T> 對(duì)象礁击。
列標(biāo)題上的鏈接使用查詢字符串將當(dāng)前的搜索字符串傳遞到控制器,以便用戶可以在過(guò)濾后的結(jié)果中進(jìn)行排序:

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

The paging buttons are displayed by tag helpers:
分頁(yè)按鈕使用 tag helpers 進(jìn)行顯示

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

運(yùn)行應(yīng)用并轉(zhuǎn)到 Student 頁(yè)面。


image.png

在不同排序狀態(tài)下點(diǎn)擊分頁(yè)鏈接哆窿,以確認(rèn)分頁(yè)功能正常工作链烈。然后嘗試搜索后再分頁(yè),驗(yàn)證分頁(yè)功能在不同排序和過(guò)濾條件下都正常工作挚躯。

創(chuàng)建一個(gè)顯示學(xué)生統(tǒng)計(jì)信息的關(guān)于頁(yè)面

在 Contoso 大學(xué)網(wǎng)站的 About 頁(yè)面强衡, 將顯示每天有多少學(xué)生進(jìn)行注冊(cè),這需要對(duì)數(shù)據(jù)進(jìn)行分組码荔,并在分組上做計(jì)算漩勤。要完成此任務(wù),您需要執(zhí)行以下操作:

  • 創(chuàng)建一個(gè)用于傳遞數(shù)據(jù)到視圖的 ViewModel 類缩搅。
  • 修改 HomeController 中的 About 方法越败。
  • 修改 About 視圖。

創(chuàng)建 ViewModel 類

Models 文件夾中創(chuàng)建一個(gè) SchoolViewModels 文件夾
在這個(gè)新的文件夾中硼瓣,添加一個(gè)文件名為 EnrollmentDateGroup.cs 的類眉尸,并輸入以下代碼:

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

修改 HomeController

HomeController.cs 文件, 頂部加入如下語(yǔ)句:

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

在類中添加一個(gè)數(shù)據(jù)庫(kù)上下文變量 _context, ASP.NET Core 依賴注入將為此變量提供實(shí)例巨双。

public class HomeController : Controller
{
    private readonly SchoolContext _context;

    public HomeController(SchoolContext context)
    {
        _context = context;
    }

將 About 方法替換為以下代碼:

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

LINQ 語(yǔ)句將 Student 實(shí)體進(jìn)行分組噪猾,計(jì)算每個(gè)分組中的實(shí)體數(shù)量,并將結(jié)果存放在 EnrollmentDateGroup ViewModel 對(duì)象中筑累。

備注

在 EF Core 1.0 版本中袱蜡, 整個(gè)結(jié)果集返回到客戶端,并在客戶端上進(jìn)行分組慢宗。在某些情況下坪蚁,這會(huì)導(dǎo)致性能問(wèn)題。請(qǐng)使用實(shí)際生產(chǎn)環(huán)境規(guī)模的數(shù)據(jù)測(cè)試性能镜沽,如有必要敏晤,使用原始 SQL 在服務(wù)器進(jìn)行分組。 有關(guān)如何使用原始的 SQL 的信息缅茉,請(qǐng)參閱本系列最后一個(gè)教程嘴脾。

修改 About 視圖

替換 Views/Home/About.cshtml 為如下代碼:

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

運(yùn)行應(yīng)用,轉(zhuǎn)至 About 頁(yè)面蔬墩。 每個(gè)日期的學(xué)生注冊(cè)數(shù)量顯示于表格中译打。


image.png

小結(jié)

在本教程中,你已了解如何執(zhí)行排序拇颅、 篩選奏司、 分頁(yè)和分組。 在下一步的教程中樟插,你將了解如何通過(guò)使用遷移來(lái)處理數(shù)據(jù)模型更改韵洋。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末竿刁,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子搪缨,更是在濱河造成了極大的恐慌食拜,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件勉吻,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡旅赢,警方通過(guò)查閱死者的電腦和手機(jī)齿桃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)煮盼,“玉大人短纵,你說(shuō)我怎么就攤上這事〗┛兀” “怎么了香到?”我有些...
    開封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)报破。 經(jīng)常有香客問(wèn)我悠就,道長(zhǎng),這世上最難降的妖魔是什么充易? 我笑而不...
    開封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任梗脾,我火速辦了婚禮,結(jié)果婚禮上盹靴,老公的妹妹穿的比我還像新娘炸茧。我一直安慰自己,他們只是感情好稿静,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開白布梭冠。 她就那樣靜靜地躺著,像睡著了一般改备。 火紅的嫁衣襯著肌膚如雪控漠。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天悬钳,我揣著相機(jī)與錄音润脸,去河邊找鬼。 笑死他去,一個(gè)胖子當(dāng)著我的面吹牛毙驯,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播灾测,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼爆价,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼垦巴!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起铭段,我...
    開封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤骤宣,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后序愚,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體憔披,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年爸吮,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了芬膝。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡形娇,死狀恐怖锰霜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情桐早,我是刑警寧澤癣缅,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站哄酝,受9級(jí)特大地震影響友存,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜陶衅,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一爬立、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧万哪,春花似錦侠驯、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至的止,卻和暖如春檩坚,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背诅福。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工匾委, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人氓润。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓赂乐,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親咖气。 傳聞我的和親對(duì)象是個(gè)殘疾皇子挨措,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • CRUD (創(chuàng)建挖滤,讀取,更新和刪除) 本系列文章介紹及索引目錄 在上一章節(jié)中浅役,您創(chuàng)建了一個(gè)使用 Entity Fr...
    程序員長(zhǎng)春閱讀 1,647評(píng)論 0 1
  • 前言 Contoso 大學(xué)示例 Web 應(yīng)用程序演示如何使用實(shí)體框架(EF)Core 2.0 和 Visual S...
    程序員長(zhǎng)春閱讀 7,662評(píng)論 1 15
  • 每一處農(nóng)作物都是從兩手空空長(zhǎng)到開花結(jié)果下一次麥?zhǔn)兆屢盎鹱兂蓧災(zāi)?/div>
    木卯丁閱讀 179評(píng)論 0 2
  • 小時(shí)候斩松,我們上學(xué),玩耍觉既,吵架惧盹,和好,唯獨(dú)不會(huì)想到有一天我們會(huì)分別瞪讼,直到那一天來(lái)臨钧椰,才痛哭流涕。 而所謂長(zhǎng)大尝艘,就是我...
    紅茶jl閱讀 152評(píng)論 0 2
  • 我不想有個(gè)家演侯,想一個(gè)人流浪姿染, 想一個(gè)人追夢(mèng) 深夜是我的保護(hù)色 我隱藏起自己背亥, 我想完成所有...
    傷情閱讀 259評(píng)論 0 1