http://www.asp.net/mvc/overview/getting-started/getting-started-with-ef-using-mvc/sorting-filtering-and-paging-with-the-entity-framework-in-an-asp-net-mvc-application
Contoso 大学示例 web 应用程序演示如何创建使用实体框架 6 代码第一次和视觉工作室 2013年的 ASP.NET MVC 5 应用程序。系列教程有关的信息,请参阅本系列第一篇教程.
在前面的教程你实施一套基本的 CRUD 操作,为Student
实体的 web 页。在本教程中,您将添加排序、 筛选和分页功能到学生索引页。您还将创建一个页面,并简单分组。
下面的插图显示页面当你完成时的外观。列标题是链接,用户可以单击要作为排序依据的列。单击列标题,一再升序和降序之间切换。
将列排序链接添加到学生索引页
若要添加排序到学生索引页面,会改变Student
控制器的Index
方法和Student
索引视图中添加代码。
添加排序功能的索引方法
在Controllers\StudentController.cs,用下面的代码替换Index
法︰
public ActionResult Index(string sortOrder) { ViewBag.NameSortParm = String.IsNullOrEmpty(sortOrder) ? "name_desc" : ""; ViewBag.DateSortParm = sortOrder == "Date" ? "date_desc" : "Date"; var students = from s in db.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(students.ToList()); }
此代码接收sortOrder
参数从 URL 中的查询字符串。由 ASP.NET MVC 作为操作方法的参数提供的查询字符串值。该参数将是一个字符串,它是"名称"日期",可以选择跟着一条下划线和字符串"desc"来指定降序排列。默认排序顺序升序。
第一次请求是索引页,那里是没有查询字符串。学生按升序排列显示的LastName
,这是默认设置,设立的秋天通过案例switch
语句。当用户单击列标题的超链接时,在查询字符串中提供适当sortOrder
值。
两个ViewBag
变量使用,查看可以配置列标题超链接与适当的查询字符串值︰
ViewBag.NameSortParm = String.IsNullOrEmpty(sortOrder) ? "name_desc" : ""; ViewBag.DateSortParm = sortOrder == "Date" ? "date_desc" : "Date";
这些都是三元的语句。第一个指定sortOrder
参数为 null 或为空,是否ViewBag.NameSortParm
应设置为"name_desc";否则,应将设置为空字符串。这两个语句启用视图,设置列标题的超链接,如下所示︰
当前排序顺序 | 最后名称超链接 | 日期的超链接 |
---|---|---|
最后名称升序排列 | 降序 | 升序 |
最后名称降序 | 升序 | 升序 |
日期升序 | 升序 | 降序 |
日期降序 | 升序 | 升序 |
该方法使用LINQ 到实体来指定要排序的列。代码创建了一个IQueryable变量之前switch
语句、 修改在switch
语句,和后switch
语句调用ToList
方法。当您创建和修改IQueryable
变量时,没有查询被发送到数据库。不执行查询,直到您转换IQueryable
对象集合通过调用一个方法如ToList
。因此,这段代码结果直到return View
的语句不执行单个查询中。
作为编写不同的 LINQ 语句为每个排序顺序的替代方法,您可以动态地创建 LINQ 语句。关于动态 LINQ 的信息,请参阅动态 LINQ.
添加列标题到学生索引视图的超链接
在Views\Student\Index.cshtml,将标题行的<tr>
和<th>
元素替换突出显示的代码︰
<p> @Html.ActionLink("Create New", "Create") </p> <table> <tr> <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> <th></th> </tr> @foreach (var item in Model) {
此代码使用ViewBag
属性中的信息来设置超链接与适当的查询字符串值。
运行页并单击最后一个名字和注册日期列标题,以验证该文献整理工作。
单击姓氏标题后,学生是降序显示最后一个名称。
向学生索引页添加一个搜索框
若要添加筛选到学生索引页,你会向视图中添加一个文本框和一个提交按钮和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.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(students.ToList()); }
您已经添加到Index
方法的searchString
参数。搜索字符串值被收到一个文本框,您将添加到索引视图。你也已经添加到 LINQ 语句where
子句选择只有其名字或姓氏包含搜索字符串的学生。只有在要搜索的值执行添加where子句的语句。
注意在许多情况下你可以调用同一个方法,在实体框架的实体集或作为对内存中集合的扩展方法。结果通常都是一样,但在某些情况下可能会有所不同。
例如,Contains
方法的.NET 框架实现返回所有行,当你将一个空字符串传递给它,但 SQL Server 紧凑 4.0 的实体框架提供程序都返回零行空字符串。因此 (放Where
中if
语句的语句) 的示例中的代码将确保你得到相同的结果,所有版本的 SQL Server。而且在此基础上, Contains
方法的.NET 框架实现默认情况下,执行区分大小写的比较,实体框架 SQL Server 提供程序在默认情况下执行不区分大小写的比较。因此,调用ToUpper
方法使测试明确不区分大小写可以确保当您更改以后要使用存储库,它将返回而不是IQueryable
对象IEnumerable
集合的代码,结果不会改变。(当你调用Contains
对IEnumerable
集合时,你得到的.NET 框架实现; 当你对IQueryable
对象调用它,你得到的数据库提供程序实现)。
空值处理也可能不同,不同的数据库供应商或当您使用IQueryable
对象相比,当你使用IEnumerable
集合。例如,在某些情况下一个Where
条件如 table.Column != 0
可能不会返回具有null
值的列。更多的信息,请参阅where 子句中的 null 变量的错误处理.
学生索引视图中添加一个搜索框
在Views\Student\Index.cshtml,先添加突出显示的代码立即开放table
标记以创建一个标题、 一个文本框和搜索按钮。
<p> @Html.ActionLink("Create New", "Create") </p> @using (Html.BeginForm()) { <p> Find by name: @Html.TextBox("SearchString") <input type="submit" value="Search" /></p> } <table> <tr>
运行页,输入搜索字符串,并单击搜索,筛选验证正常工作。
请注意 URL 不包含"一个"搜索字符串,这意味着,如果您收藏此页,你不会得到筛选后的列表,当您使用书签。您将更改搜索按钮,稍后在本教程中使用查询字符串进行筛选条件。
向学生索引页添加分页
要向学生索引页添加分页,你就会开始安装PagedList.Mvc NuGet 包。然后你会在Index
法进行其他更改和添加分页链接到Index
视图。PagedList.Mvc是一个多好的分页和排序包为 ASP.NET MVC 中,和它的使用在这里仅用作示例,不是为它在其他选项的建议。下面的插图显示分页链接。
安装 PagedList.MVC NuGet 程序包
NuGet PagedList.Mvc包自动安装PagedList软件包,作为一种依赖。PagedList包安装IQueryable
和IEnumerable
集合PagedList
集合类型和扩展方法。扩展方法在你IQueryable
或IEnumerable
, PagedList
集合中创建单个数据页和PagedList
集合提供几个属性和便利分页的方法。PagedList.Mvc包安装一个分页的助手显示分页按钮。
从工具菜单中,选择库软件包管理器,然后程序包管理器控制台.
在程序包管理器控制台窗口中,确保 ghe软件包源是nuget.org ,默认项目是ContosoUniversity,,然后输入以下命令︰
Install-Package PagedList.Mvc
生成项目。
将分页功能添加到索引方法
在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 (searchString != null) { page = 1; } else { searchString = currentFilter; } ViewBag.CurrentFilter = searchString; var students = from s in db.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: // Name ascending students = students.OrderBy(s => s.LastName); break; } int pageSize = 3; int pageNumber = (page ?? 1); return View(students.ToPagedList(pageNumber, pageSize)); }
此代码将添加一个page
参数,当前的排序顺序参数和当前的筛选器参数的方法签名︰
public ActionResult Index(string sortOrder, string currentFilter, string searchString, int? page)
第一次显示的页面,或者如果用户还没有单击分页或排序链接,所有的参数将为 null。如果单击分页链接,page
变量将包含要显示的页面编号。
A ViewBag
属性提供的视图与当前的排序顺序,因为这必须列入分页链接以保持排序顺序在分页时相同︰
ViewBag.CurrentSort = sortOrder;
另一个属性, ViewBag.CurrentFilter
,提供的视图的当前筛选器字符串。此值必须列入分页链接以保持筛选器设置分页,过程中,它必须将还原到文本框中时重新显示的页面。如果分页过程中更改搜索字符串,则该页面具有重置为 1,因为新的筛选器可能会导致不同的数据显示。在文本框中输入值并按提交按钮时,将改变搜索字符串。在这种情况下, searchString
参数不是空的。
if (searchString != null) { page = 1; } else { searchString = currentFilter; }
在方法的末尾,学生IQueryable
对象的ToPagedList
扩展方法将学生查询转换为学生在支持分页的集合类型的单个网页。学生该单个页面然后传递给视图︰
int pageSize = 3; int pageNumber = (page ?? 1); return View(students.ToPagedList(pageNumber, pageSize));
ToPagedList
方法采用页面数目。两个问号代表null 合并运算符。Null 合并运算符为定义了默认值为 null 的类型;该表达式(page ?? 1)
手段返回page
的值,如果它的值,或如果page
是 null,则返回 1。
向学生索引视图添加分页链接
在Views\Student\Index.cshtml,用下面的代码替换现有代码。突出的变化。
@model PagedList.IPagedList<ContosoUniversity.Models.Student> @using PagedList.Mvc; <link href="~/Content/PagedList.css" rel="stylesheet" type="text/css" /> @{ ViewBag.Title = "Students"; } <h2>Students</h2> <p> @Html.ActionLink("Create New", "Create") </p> @using (Html.BeginForm("Index", "Student", FormMethod.Get)) { <p> Find by name: @Html.TextBox("SearchString", ViewBag.CurrentFilter as string) <input type="submit" value="Search" /> </p> } <table class="table"> <tr> <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> <th></th> </tr> @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> @Html.ActionLink("Edit", "Edit", new { id=item.ID }) | @Html.ActionLink("Details", "Details", new { id=item.ID }) | @Html.ActionLink("Delete", "Delete", new { id=item.ID }) </td> </tr> } </table> <br /> Page @(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber) of @Model.PageCount @Html.PagedListPager(Model, page => Url.Action("Index", new { page, sortOrder = ViewBag.CurrentSort, currentFilter = ViewBag.CurrentFilter }))
在页面顶部的@model
语句指定视图现在获取PagedList
对象而不是一个List
对象。
PagedList.Mvc
的using
语句,访问分页按钮的 MVC 帮手。
该代码使用BeginForm ,使它能够指定FormMethod.Get过载.
@using (Html.BeginForm("Index", "Student", FormMethod.Get)) { <p> Find by name: @Html.TextBox("SearchString", ViewBag.CurrentFilter as string) <input type="submit" value="Search" /> </p> }
默认值BeginForm提交表单数据同一个职位,这意味着参数作为查询字符串传递 HTTP 邮件正文中,不在 URL 中。当您指定 HTTP GET 时,表单数据被通过在 URL 中作为查询字符串,使用户能够创建 URL 的书签。W3C 的 HTTP GET 使用指南推荐你应该使用 GET,当行动不会导致更新。
所以当你点击新的一页,你可以看到当前的搜索字符串,用当前的搜索字符串初始化文本框。
Find by name: @Html.TextBox("SearchString", ViewBag.CurrentFilter as string)
列标题链接使用查询字符串传递给控制器的当前搜索字符串,以便用户可以在筛选结果中排序︰
@Html.ActionLink("Last Name", "Index", new { sortOrder=ViewBag.NameSortParm, currentFilter=ViewBag.CurrentFilter })
显示页面的当前页和总数目。
Page @(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber) of @Model.PageCount
如果有没有要显示的页,则显示"0 0 页"。(在这种情况下页编号大于页计数因为Model.PageNumber
是 1,并且Model.PageCount
是 0)。
由PagedListPager
助手显示分页按钮︰
@Html.PagedListPager( Model, page => Url.Action("Index", new { page }) )
PagedListPager
助手提供了大量的选项,您可以自定义,包括 Url 和造型。有关更多信息,请参见TroyGoode / PagedList GitHub 网站上。
运行页。
单击不同的排序顺序,使确定分页作品中的分页链接。然后输入搜索字符串并试分页再次来验证分页排序和过滤还可以正常工作。
创建关于显示学生统计信息的页面
为 Contoso 大学网站的网页,您将显示有多少学生为每个注册日期。这就需要对群体的分组和简单计算。要做到这一点,就会执行以下操作︰
- 为创建一个视图模型类的数据,您需要传递给视图。
- 修改
Home
控制器中的About
方法。 - 修改
About
视图。
创建视图模型
在项目文件夹中创建一个Viewmodel文件夹。在该文件夹中添加一个类文件EnrollmentDateGroup.cs和模板代码替换为以下代码︰
using System; using System.ComponentModel.DataAnnotations; namespace ContosoUniversity.ViewModels { public class EnrollmentDateGroup { [DataType(DataType.Date)] public DateTime? EnrollmentDate { get; set; } public int StudentCount { get; set; } } }
修改主控制器
在HomeController.cs,在文件的顶部添加以下using
语句︰
using ContosoUniversity.DAL; using ContosoUniversity.ViewModels;
为类大括号后立即添加数据库上下文类变量︰
public class HomeController : Controller { private SchoolContext db = new SchoolContext();
About
方法替换为以下代码︰
public ActionResult About() { IQueryable<EnrollmentDateGroup> data = from student in db.Students group student by student.EnrollmentDate into dateGroup select new EnrollmentDateGroup() { EnrollmentDate = dateGroup.Key, StudentCount = dateGroup.Count() }; return View(data.ToList()); }
在 LINQ 语句按招生日期分组学生实体、 计算每个组中的实体的数目和将结果存储在EnrollmentDateGroup
视图模型对象的集合。
添加一个Dispose
的方法︰
protected override void Dispose(bool disposing) { db.Dispose(); base.Dispose(disposing); }
修改关于视图
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> @Html.DisplayFor(modelItem => item.EnrollmentDate) </td> <td> @item.StudentCount </td> </tr> } </table>
运行应用程序并单击关于链接。学生每个招生日期的计数显示在表中。
摘要
在本教程中,您看到了如何创建一个数据模型和实现基本的 CRUD,排序、 筛选、 分页和分组功能。在接下来的教程中,您将开始看更高级的主题,通过扩展数据模型。
请留下反馈,关于如何你喜欢本教程,我们可以提高。您也可以要求在展示我如何用代码的新主题.
其他实体框架资源的链接可以找到在ASP.NET 数据访问-推荐资源.
这篇文章的初衷是在 2014 年 2 月 14 日