ASP.NET Core 和 EF Core 系列教程——排序、筛选、分页和分组(3 / 10)
作者:Tom Dykstra和Rick Anderson
Contoso 大学示例 web 应用程序演示如何使用 Entity Framework Core 和 Visual Studio 创建 ASP.NET Core MVC web 应用程序。 想要获取有关系列教程的信息,请参阅第一个教程。
在前面的教程,你可以实现一组的用于学生实体的基本 CRUD 操作网页。 在本教程将向学生索引页添加排序、 筛选和分页功能。 你还将创建具有简单分组功能的页面。
下图显示你完成本教程后相关页面的样子。 列标题时一个链接,用户可以单击它使数据按该列排序。 反复单击列标题在升序排列和降序排列之间切换。
将列排序链接添加到学生索引页
为了添加排序学生索引页,你将更改学生控制器中的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参数。 ASP.NET Core MVC 提供的查询字符串作为参数传递给的操作方法。 “Name”或”Date”,后面可以选择性跟用于指定降序顺序的下划线和”desc”构成参数字符串。 默认排序顺序为升序。
第一次请求索引页时,没有任何查询字符串。 学生按姓氏升序显示也就是switch语句中的缺省值中的排序方式。 当用户单击列标题的超链接,将向Index方法提供相应的sortOrder查询字符串。
视图使用ViewData元素中两个元素 (NameSortParm 和 DateSortParm) 对应的查询字符串值配置列标题超链接。 - 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());
- }
复制代码
这两个语句都使用了三目运算符。 第一个语句指如果sortOrder参数为 null 或为空则 NameSortParm 应设置为”name_desc”; 否则,它应设置为一个空字符串。 这两个语句使试图能够如下所示设置列标题的超链接:
当前的排序顺序 | Last Name 超链接 | Date 超链接 |
---|
Last Name 升序排列 | descending | ascending | Last Name 降序排列 | ascending | ascending | Date 升序排列 | ascending | descending | Date 降序排列 | ascending | ascending |
该方法使用 LINQ to Entities 指定要作为排序依据的列。 代码在switch 语句之前创建了IQueryable变量然后在 switch 语句中对其进行修改,并在switch语句之后调用ToListAsync方法。 当你创建和修改IQueryable变量时数据库不会接收到任何查询。 在您调用如ToListAsync等方法将IQueryable转换为集合对象之前不会执行查询。 因此,在return View语句之前此代码只会执行一个查询。
此代码会获得具有大量列的冗长信息。 本系列最后一个教程将演示如何编写代码,使你可以使用字符串将需要OrderBy的行的名称作为参数传递给方法。
向学生索引视图的列标题添加超链接
用以下代码替换Views/Students/Index.cshtml,以添加列标题超链接。 高亮代码为已更改的行。 - @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>
复制代码
代码中使用了ViewData元素中的信息来以相应的查询字符串值设置超链接。
运行应用程序中,选择Students卡,然后单击Last Name和Enrollment Date列标题,以验证该排序成功。
向学生索引页添加搜索框
向视图添加一个文本框和提交按钮来向索引页添加搜索框,并在Index方法中做相应更改。 你可以在文本框中输入字符串搜索名字和姓氏字段中的内容。
向索引方法添加筛选功能
在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参数。 搜索字符串值来自之后会添加到索引视图中的文本框。 你还向 LINQ 语句 添加了where 子句来选择仅名字或姓氏包含搜索字符串的学生。 添加 where 子句的语句只有在要搜索值的时候才执行。
此处对IQueryable对象调用Where方法,筛选器将在服务器上处理。 在某些场景下你可能会对内存中集合调用作为扩展方法的Where。 (例如,假设你使用_context.Students引用,不同于 EFDbSet,它返回IEnumerable集合的存储库方法的引用。)结果通常将相同,但在某些情况下可能会不同。
例如,默认情况下 .NET Framework 实现的Contains方法是对大小写敏感的,但 SQL Server 中由 SQL Server 的排序规则确定。 SQL Server 默认不区分大小写。 您可以调用ToUpper方法使得其大小写敏感:Where (s = > s.LastName.ToUpper()。Contains(searchString.ToUpper())。 这样做能确保如果将来使用返回IEnumerable集合的存储库方法而不是IQueryable对象来修改相关代码,结果还能保持相同。 (当你对IEnumerable集合调用Contains方法,你将获取.NET Framework 的实现; 当对IQueryable对象调用它,则会得到数据库驱动的实现。)但是,此解决方案会对性能产生负面影响。 ToUpper将函数加入到 TSQL SELECT 语句的 WHERE 子句中。 这样做会是的索引优化失去效果。 假设 SQL 大多是是大小写不敏感,在你将数据迁移到大小写敏感的数据存储库之前最好避免ToUpper代码。
向学生索引视图添加一个搜索框
打开Views/Student/Index.cshtml,在table标签之前添加高亮代码以在页面中创建标题,Search文本框,按钮。 - <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">
复制代码
此代码通过使用 |