Contoso 大学 - 3 - 排序、过滤及分页
在上一个课程中,我们已经学习了如何使用 EF 对 Student 实体进行增、删、改、查处理。这次的课程我们将对学生的 Index 页面加入排序、过滤以及分页的功能。还要创建一个页面完成简单的分组。
下面的截图展示了完成之后的页面,列的标题作为链接支持用户通过点击完成排序,点击标题可以在升序和降序之间进行切换。
3-1 在 Students 的 Index 页面增加列标题链接
为 Index 页面增加排序的功能,我们需要修改 Student 控制器的 Index 方法,还需要为 Student 视图增加代码。
3-1-1 为 Index 方法增加排序功能
打开 Controllers\StudentController.cs,将 Index 方法替换为如下的代码。
public ViewResult Index(string sortOrder){ ViewBag.NameSortParm = String.IsNullOrEmpty(sortOrder) ? "Name desc" : ""; ViewBag.DateSortParm = sortOrder == "Date" ? "Date desc" : "Date";var students = from s in db.Studentsselect 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(students.ToList());}
这段代码从 URL 中接收名为 sortOrder 的参数,这个参数由 ASP.NET MVC 作为参数传递给 Action 方法。这个参数可以是 “Name” 或者 “Date”, 可能还有一个空格隔开的 desc 来指定降序。
当第一次请求 Index 的时候,没有参数,学生使用 LastName 的升序顺序显示。这是通过 switch 的 default 代码段指定的,当用户点击一个列的标题链接的时候,合适的 sortOrder 值需要通过查询字符串传递进来。
两个 ViewBag 变量用来为视图提供合适的查询字符串链接值。
ViewBag.NameSortParm = String.IsNullOrEmpty(sortOrder) ? "Name desc" : "";ViewBag.DateSortParm = sortOrder == "Date" ? "Date desc" : "Date";
这里使用了条件语句,第一个用来指定当 sortOrder 参数为 null 或者空串的时候, ViewBag.NameSortParm 应用被设置为 Name desc,其他情况下,应该被设置为空串。
这里有四种可能,依赖于当前的排序情况:
- 如果当前的排序规则为 LastName 升序,那么,LastName 链接应该设置为降序,Enrollment Date 链接必须被设置为按日期升序。
- 如果当前的排序规则为 LastName 降序,那么,LastName 链接应该设置为升序,排序串应该为空串,日期为升序。
- 如果当前排序的规则为 Date 升序,那么,链接应该为 LastName 升序和日期升序。
- 如果当前的排序规则为 Date 降序,那么,链接应该为 LastName 升序和日期降序。
方法中使用 LINQ to Entities 来指定排序,在 switch 之前,代码首先创建一个 IQueryable 变量,在 switch 语句中修改这个查询表达式,最后调用 ToList 方法。在创建和修改查询表达式 IQueryable 的时候,并没有将查询发送到数据库中执行,查询直到将 IQueryable 对象驼工调用类似 ToList 方法转换到集合对象的时候才会执行,因此,代码中查询直到最后的 return View 才会被执行。
3-2-2 为 Index 视图增加列标题链接
在 Views\Student\Index.cshtml,使用如下的代码替换标题行中的 <tr> 和 <th> 元素。
<tr> <th></th> <th> @Html.ActionLink("Last Name", "Index", new { sortOrder=ViewBag.NameSortParm }) </th> <th> First Name </th> <th> @Html.ActionLink("Enrollment Date", "Index", new { sortOrder=ViewBag.DateSortParm }) </th></tr>
这段代码使用 ViewBag 属性来设置超级链接中包含适当的查询字符串。
运行页面,点击列标题,来验证排序是否正常。
3-2 为 Index 页面增加搜索框
为 Index 页面增加过滤功能,需要增加一个文本框和一个提交按钮,然后,对 Index 方法进行一些修改,文本框允许你输入一个搜索字符串,用来在 FirstName 和 LastName 中进行搜索。
3-2-1 为 Index 方法增加过滤功能
打开 Controllers\StudentController.cs 文件,使用下面的代码替换 Index 方法。
public ViewResult Index(string sortOrder, string searchString){ ViewBag.NameSortParm = String.IsNullOrEmpty(sortOrder) ? "Name desc" : ""; ViewBag.DateSortParm = sortOrder == "Date" ? "Date desc" : "Date";var students = from s in db.Studentsselect s;if (!String.IsNullOrEmpty(searchString)) { students = students.Where(s => s.LastName.ToUpper().Contains(searchString.ToUpper()) || s.FirstMidName.ToUpper().Contains(searchString.ToUpper())); }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(students.ToList());}
现在,为 Index 方法增加了一个参数 searchString ,LINQ 语句中也增加了一个 where 子句,用来选择在 FirstName 或者 LastName 中包含过滤字符串的学生。搜索串来自文本框的输入,后面需要你在视图中加入它。增加的 where 条件子句仅仅在提供了搜索串的情况下才会被处理。
if (!String.IsNullOrEmpty(searchString)){ students = students.Where(s => s.LastName.ToUpper().Contains(searchString.ToUpper()) || s.FirstMidName.ToUpper().Contains(searchString.ToUpper()));}
注意:在传递一个空串的时候,.NET 实现的 Contains 方法将会返回所有的数据行,但是 EF Provider for SQL Server Compact 4.0 对于空串不返回任何行。因此,代码中增加了一个 if 判断语句,以确保对于所有的 SQL Server 都有一致的处理结果。另外,.NET 实现的 Contains 默认进行区分大小写的字符串比较,因此,通过调用 ToUpper 方法显式转换字符串为大写,
以确保在转换到使用资源库模式的时候不需要修改代码。那个时候将会返回一个 IEnumerable 集合而不是 IQueryable 对象 ( 在调用 IEnumerable 集合上的 Contains 方法的时候,使用 .NET 实现的方法,在调用 IQueryable 对象上的 Contains 方法的时候,使用数据库 Provider 提供的实现 )。
3-2-2 在 Student 视图上加入搜索框
在视图 Views\Student\Index.cshtml 上,table 开始标记之前,增加一个标题,一个文本框,以及一个 Search 按钮。
@using (Html.BeginForm()){ <p> Find by name: @Html.TextBox("SearchString") <input type="submit" value="Search" /></p>}
运行程序,输入一个搜索串,然后点击 Search 按钮来查看过滤的效果。
3-3 在 Student 的 Index 视图上增加分页
为了支持分页,你需要通过 NuGet 包管理器安装 PagedList ,然后,需要在 Index 方法中增加一些代码,在视图中增加分页的链接,下面的截图展示了分页的链接。
3-3-1 安装 PagedList 包
NuGet 中的 PagedList 包将会增加一种类型:PagedList,当将查询结果传入到 PagedList 中后,它提供的一系列属性和方法使得排序更加简单。
在 Visual Studio 中,确信选中了当前的项目,而不是解决方案。在 Tools 菜单中,选择 Library Package Manager,然后选择 Add Library Package Reference。
在 Add Library Package Reference 对话框中,点击左边的 Online 窗格,然后在搜索框中输入 pagedlist ,在看到 PagedList 包之后,点击 Install。
3-3-2 为 Index 方法增加分页功能
打开 Controllers\StudentController.cs,在代码的前面为 PagedList 命名空间增加 using 语句.
using PagedList;
将 Index 方法替换成如下的代码。
public ViewResult Index(string sortOrder, string currentFilter, string searchString, int? page) { ViewBag.CurrentSort = sortOrder; ViewBag.NameSortParm = String.IsNullOrEmpty(sortOrder) ? "Name desc" : ""; ViewBag.DateSortParm = sortOrder == "Date" ? "Date desc" : "Date"; if (Request.HttpMethod == "GET") { searchString = currentFilter; }else { page = 1; } ViewBag.CurrentFilter = searchString; var students = from s in db.Studentsselect s;if (!String.IsNullOrEmpty(searchString)) { students = students.Where(s => s.LastName.ToUpper().Contains(searchString.ToUpper()) || s.FirstMidName.ToUpper().Contains(searchString.ToUpper())); }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;int pageNumber = (page ?? 1);return View(students.ToPagedList(pageNumber, pageSize)); }
方法又增加了一个 page 参数,方法的签名如下所示。
public ViewResult Index(string sortOrder, string currentFilter, string searchString, int? page)
当第一次显式这个页面的时候,或者用户没有点击分页链接的时候,page 参数将会是 null。如果分页链接被点击了,page 参数将会包含需要显示的页码。
ViewBag 中的 CurrentSort 属性用来提供当前的排序顺序,它必须被包含到当前的分页链接中,以便在分页处理过程中保持当前的排序规则。
ViewBag.CurrentSort = sortOrder;
其它的 ViewBag 属性为视图提供当前的过滤串,因为这个过滤串在页面被重新显示的时候,必须重新回到文本框中,另外,这个串也必须包含在分页链接中,以便在分页过程中,保持过滤效果。最后,如果在分页的过程中修改了过滤串,那么页码将会回到第一页,因为新的过滤规则返回了不同的数据,很可能原来的页码在这时候已经不再存在了。
if (Request.HttpMethod == "GET"){ searchString = currentFilter;}else{ page = 1;}ViewBag.CurrentFilter = searchString;
在方法的最后,查询学生的表达式被转换为 PagedList ,而不再是通常的 List,这样传递到视图中的就是支持分页的集合,代码如下:
int pageSize = 3;int pageNumber = (page ?? 1);return View(students.ToPagedList(pageNumber, pageSize));
ToPagedList 方法需要一个页码值,两个问号用来为可空的页码提供一个默认值,表达式 ( page ?? 1 ) 意味着如果 page 有值得话返回这个值,如果是 null 的话,返回 1。
3-3-3 为视图增加分页链接
在 Views\Student\Index.cshtml中,使用下面的代码替换原有代码。
@model PagedList.IPagedList<ContosoUniversity.Models.Student> @{ ViewBag.Title = "Students";} <h2>Students</h2> <p> @Html.ActionLink("Create New", "Create")</p>@using (Html.BeginForm()){ <p> Find by name: @Html.TextBox("SearchString", ViewBag.CurrentFilter as string) <input type="submit" value="Search" /></p>}<table><tr> <th></th> <th> @Html.ActionLink("Last Name", "Index", new { sortOrder=ViewBag.NameSortParm, currentFilter=ViewBag.CurrentFilter }) </th> <th> First Name </th> <th> @Html.ActionLink("Enrollment Date", "Index", new { sortOrder = ViewBag.DateSortParm, currentFilter = ViewBag.CurrentFilter }) </th></tr> @foreach (var item in Model) { <tr> <td> @Html.ActionLink("Edit", "Edit", new { id=item.StudentID }) | @Html.ActionLink("Details", "Details", new { id=item.StudentID }) | @Html.ActionLink("Delete", "Delete", new { id=item.StudentID }) </td> <td> @Html.DisplayFor(modelItem => item.LastName) </td> <td> @Html.DisplayFor(modelItem => item.FirstMidName) </td> <td> @Html.DisplayFor(modelItem => item.EnrollmentDate) </td> </tr>} </table> <div> Page @(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber) of @Model.PageCount @if (Model.HasPreviousPage) { @Html.ActionLink("<<", "Index", new { page = 1, sortOrder = ViewBag.CurrentSort, currentFilter=ViewBag.CurrentFilter }) @Html.Raw(" "); @Html.ActionLink("< Prev", "Index", new { page = Model.PageNumber - 1, sortOrder = ViewBag.CurrentSort, currentFilter=ViewBag.CurrentFilter }) }else { @:<< @Html.Raw(" "); @:< Prev } @if (Model.HasNextPage) { @Html.ActionLink("Next >", "Index", new { page = Model.PageNumber + 1, sortOrder = ViewBag.CurrentSort, currentFilter=ViewBag.CurrentFilter }) @Html.Raw(" "); @Html.ActionLink(">>", "Index", new { page = Model.PageCount, sortOrder = ViewBag.CurrentSort, currentFilter=ViewBag.CurrentFilter }) }else { @:Next > @Html.Raw(" ") @:>> }</div>
视图最前面的 @model 语句指定现在传递到视图的不再是 List 而是 PagedList 。
文本框使用当前的搜索串进行初始化,以便在分页的时候不会丢失搜索串。
Find by name: @Html.TextBox("SearchString", ViewBag.CurrentFilter as string)
列的标题链接使用查询串来传递当前的搜索串,以便传递给控制器当前的搜索和排序。
@Html.ActionLink("Last Name", "Index", new { sortOrder=ViewBag.NameSortParm, currentFilter=ViewBag.CurrentFilter })
在当前页面的最后,通过一行来显示分页的导航 UI。
Page [current page number] of [total number of pages] << < Prev Next > >>
<< 符号连接到第一页, < Prev 链接到上一页,等等。如果用户当前就在第一页,那么,链接到第一页的链接就会被禁用,类似地,如果用户当前在最后一页,导航到最后一页就会被禁用,每一个分页链接传递页码以及当前的排序串和搜索串到控制器,这使得可以在分页的同时维护排序和过滤规则。
如果没有页可以显示,将会显示 “Page 0 of 0 “,在这种情况下,页面数字就会大于页数,因为 Model.PageNumber 是 1,但是 Model.PageCount 为 0。
运行页面。
在不同的排序规则下,点击分页链接,确认分页在正常工作。然后输入一个过滤串,再次点击分页的链接,确认在排序和过滤的同时,分页可以正常工作。
3-4 创建 About 页面显示学生的统计情况
在 Contoso 大学网站的 About 页面,我们希望能够显示每个注册日有多少学生注册。这需要进行分组,然后在每个组上进行简单地计算,需要完成下列工作:
- 创建用于传递数据到视图的 ViewModel
- 修改 Home 控制器中的 About 方法
- 修改 About 视图
3-4-1 创建 ViewModel
创建 ViewModels 文件夹,在文件夹中,创建 EnrollmentDateGroup.cs 类文件,将代码替换为如下代码:
using System;using System.Collections.Generic;using System.ComponentModel.DataAnnotations; namespace ContosoUniversity.ViewModels{public class EnrollmentDateGroup { [DisplayFormat(DataFormatString = "{0:d}")]public DateTime? EnrollmentDate { get; set; } public int StudentCount { get; set; } }}
3-4-2 修改 Home 控制器
增加如下的 using 语句。
using ContosoUniversity.DAL;using ContosoUniversity.Models;using ContosoUniversity.ViewModels;
增加一个数据库上下文变量。
private SchoolContext db = new SchoolContext();
使用如下的代码替换 About 方法。
public ActionResult About(){var data = from student in db.Students group student by student.EnrollmentDate into dateGroupselect new EnrollmentDateGroup() { EnrollmentDate = dateGroup.Key, StudentCount = dateGroup.Count() };return View(data);}
LINQ 语句通过注册日期对学生进行分组,计算每一组中的实体数量,最后将查询结果保存为 EnrollmentDateGroup
对象。
3-4-3 增加 Dispose 方法
protected override void Dispose(bool disposing){ db.Dispose();base.Dispose(disposing);}
3-4-4 修改 About 视图
打开 Views\Home\About.cshtml ,替换为如下代码。
@model IEnumerable<ContosoUniversity.ViewModels.EnrollmentDateGroup> @{ ViewBag.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> @String.Format("{0:d}", item.EnrollmentDate) </td> <td> @item.StudentCount </td> </tr>}</table>
运行页面,每个注册日注册学生的数量显示在表格中。
现在,你已经看到了如何创建数据模型,以及实现基本的增、删、改、查处理,排序、过滤、分页和分组功能。下一次,我们将会扩展数据模型开始更加高级的内容。
转载于:https://www.cnblogs.com/itjeff/p/4140704.html
Contoso 大学 - 3 - 排序、过滤及分页相关推荐
- jpa 分页 排序 过滤_使用JPA标准@ViewScoped通过分页,过滤和排序进行Primefaces DataTable延迟加载...
jpa 分页 排序 过滤 Primefaces数据表惰性分页有效,但是在Web上使用Criteria搜索完整示例后,我感到非常沮丧. 所以我混合了来自 http://stackoverflow.com ...
- SQL条件查询,分组查询,排序查询,分页查询
DQL查询操作(条件查询,分组查询,排序查询,分页查询) 文章目录 DQL查询操作(条件查询,分组查询,排序查询,分页查询) 一.查询语法 二.基础查询 三.条件查询 四.排序查询 五.分组查询 六. ...
- php 点击表头排序,TP5+ajax实现点击表格表头切换排序,带分页
TP5+ajax实现点击表格表头切换排序,带分页 2018-08-15 14:59:06ThinkPHP php接收页码请求的地址 /** * ajax 无刷新分页 * param $page int ...
- 演练5-3:Contoso大学校园管理系统3
在前面的教程中,我们使用了一个简单的数据模型,包括三个数据实体.在这个教程汇中,我们将添加更多的实体和关系,按照特定的格式和验证规则等自定义数据模型. Contoso大学校园管理系统的数据模型如下. ...
- ASP.NET Core Web API基于RESTFul APIs的集合结果过滤和分页
译者荐语:如何在RESTFul APIs中进行集合结果分页?还是用客户端来拼接链接地址么? 原文来自互联网,由长沙DotNET技术社区[邹溪源]翻译.如译文侵犯您的版权,请联系小编,小编将在24小时内 ...
- 使用nhmicro使分库分表支持排序后的分页查询
2019独角兽企业重金招聘Python工程师标准>>> 常见的在创建数据时根据规则分库分表保存,查询时往往只支持按照id进行查询. Nhmicro(开源地址为 https://git ...
- APIView(认证,权限,限流,过滤,分页,序列化,排序)
概述: 一.身份认证: 1: 配置身份认证后端:注意:只作用于DRF视图,对django视图不起作用. 主要有两个认证后端,一个是基本认证后端,一个是seesion认证后端.BasicAuthenti ...
- asp.net core 排序过滤分页组件:sieve(2)表达式树的复习
在Sieve组件中使用了很多关于表达式树的知识,但在我们日常的工作中写表达式树的机会是非常少的,至少在我的编程生涯中没怎么写过表达式树(可能也就是3,4次).所以,为了能够看懂Sieve里面的源代码, ...
- 用ASP.NET Core 2.1 建立规范的 REST API -- 翻页/排序/过滤等
本文所需的一些预备知识可以看这里: 用ASP.NET Core 2.0 建立规范的 REST API -- 预备知识 和 用ASP.NET Core 2.0 建立规范的 REST API -- 预 ...
最新文章
- linux下基本命令
- 全国计算机等级考试培训方案,第23次全国计算机等级考试培训安排.doc
- 初探性能优化——2个月到4小时的性能提升
- InnoDB的ib_logfile写入策略
- 北邮高级语言设计基于java期末_北邮《高级语言程序设计》第三次阶段作业带答案...
- 「造个轮子」——cicada(轻量级 WEB 框架)
- 其他脚本与 asp.net 脚本一起验证时容易出的问题
- WebSocket简单使用
- 通信原理 —— 绪论
- ELK + kafka 分布式日志解决方案
- ueditor 编辑器增加css样式_ueditor编辑器实现语法高亮
- Android Java编写布局
- linux 查看分区
- 学生管理系统(源码)(C语言版)
- 《第一行代码》第三版之我的第一行Android代码(一)
- 初次汇编程序 masm5
- ArcGIS教程 - 9 矢量数据空间分析
- 开发者头条(一):导航页
- 中国物流市场趋势报告、技术动态创新及市场预测
- 【互联网营销基础知识】新媒体工具亲民替代款有什么?
热门文章
- 【python】匿名函数与装饰器
- 域间套接字socketpair
- 一个好的web前端开发者,是怎么学习的?
- 学会这6个强大的CSS选择器,将真正帮你写出干净的CSS代码!
- C - 数据结构实验之栈与队列三:后缀式求值
- 微信小程序怎么扩展服务器,小程序服务器可扩展的配置方案
- 轴固定位置_全面总结:轴零件固定方式及特点
- 支付宝开放平台开发助手_支付宝:如何创建和接入支付宝电脑网站支付-新手必备...
- 清空div中的内容而不刷新整个页面_Vue中的$nextTick机制
- 关于STM32系列单片机中断触发时间问题