EntityFramework_MVC4中EF5 新手入门教程之三 ---3.排序、 筛选和分页

在前面的教程你实施了一套基本的 CRUD 操作,为Student实体的 web 页。在本教程中,您将添加排序、 筛选和分页到 StudentsIndex的功能。您还将创建一个页面,并简单分组。

下面的插图显示页面当你完成时的样子。列标题是链接,用户可以单击要作为排序依据的列。单击列标题,一再升序和降序之间切换。

Students_Index_page_with_paging

将列排序链接添加到学生索引页

若要添加排序到学生索引页,会改变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";否则,应将设置为空字符串。这两个语句使该视图设置列标题的超链接,如下所示:

当前排序顺序最后一个名称超链接日期的超链接
Last Name升序排列 降序 升序
Last Name降序 升序 升序
Date升序 升序 降序
Date降序 升序 升序

该方法使用LINQ to 实体来指定要作为排序依据的列。代码创建IQueryable变量switch语句之前,修改在switch语句,并ToList调用后switch语句。当您创建和修改IQueryable变量时,没有查询发送到数据库中。不执行查询,直到您将IQueryable对象转换成一个集合通过调用一种方法如ToList因此,这段代码结果中的单个查询,return View的语句之前不会被执行。

添加列标题到学生的索引视图的超链接

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属性中的信息来设置适当的查询字符串值的超链接。

运行此页,然后单击Last NameEnrollment Date的列标题,以验证该文献整理工作。

Students_Index_page_with_sort_hyperlinks

单击姓氏标题后,学生是降序显示最后一个名称。

向学生索引页添加一个搜索框

若要添加筛选到学生索引页,将会向视图中添加一个文本框和一个提交按钮和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.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 语句whereclausethat 选择只有其名字或姓氏包含搜索字符串的学生。搜索字符串值被收到一个文本框,您将添加到索引视图。 添加where子句的语句执行只有在要搜索的值。

在许多情况下你可以调用同一个方法,在实体框架的实体集或作为一种对内存中集合的扩展方法。结果通常都是一样,但在某些情况下可能会有所不同。例如,Contains方法的.NET 框架实现返回的所有行时将为空字符串传递给它,但 SQL Server 紧凑 4.0 的实体框架提供程序都返回零行空字符串。因此 (放Whereif语句的语句) 的示例中的代码将确保你得到相同的结果,所有版本的 SQL Server。而且在此基础上, Contains方法的.NET 框架实现默认情况下,执行区分大小写的比较,实体框架 SQL Server 提供程序在默认情况下执行不区分大小写的比较。因此,调用ToUpper方法使测试明确不区分大小写可以确保当您更改以后要使用的存储库,它将返回而不是IQueryable对象IEnumerable集合的代码结果不会改变。(当Contains调用对IEnumerable集合时,你得到的.NET 框架实现 ; 当你对IQueryable对象调用它,你得到的数据库提供程序实现。

学生的索引视图中添加一个搜索框

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>
 

运行该页面,输入搜索字符串,单击搜索筛选验证正常工作。

Students_Index_page_with_search_box

请注意 URL 不包含"一个"搜索字符串,这意味着如果用书签标记此页,你不会得到筛选后的列表,当您使用该书签。您将更改搜索按钮,稍后在本教程中使用查询字符串进行筛选条件。

将分页添加到学生index页

要向学生索引页添加分页,你会开始通过安装PagedList.Mvc NuGet 程序包。然后你会在Index法进行其他更改和添加分页链接到Index视图。PagedList.Mvc是一个多好的分页和排序包为 ASP.NET MVC 中,和它的使用在这里仅用作示例,不是为它在其他选项的建议。下面的插图显示分页链接。

Students_index_page_with_paging

安装 PagedList.MVC NuGet 程序包

NuGet PagedList.Mvc包自动安装PagedList软件包,作为一种依赖。PagedList程序包安装IQueryableIEnumerable收藏PagedList集合类型和扩展方法。扩展方法在您IQueryableIEnumerable, PagedList集合中创建单个数据页和PagedList集合提供了若干属性和便利分页的方法。PagedList.Mvc包安装一个分页的助手显示分页按钮。

工具菜单中,选择库程序包管理器,然后管理 NuGet 程序包的解决方案.

管理 NuGet 程序包对话框中,单击联机选项卡在左边,然后在搜索框中输入"paged"。当你看到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.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:  // 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>
<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>
<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的对象。

using语句为PagedList.Mvc ,访问分页按钮的 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 的书签。的 HTTP GET 使用 W3C 指引列明时行动不会导致更新的情况下,您才应该使用 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 网站上。

运行页。

Students_index_page_with_paging

单击不同的排序顺序,使确保分页作品中的分页链接。然后输入搜索字符串并试分页再次来验证分页排序和过滤也可以正常工作。

创建关于显示学生的统计信息的页面

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

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>

运行应用程序并单击关于链接。学生对每个注册日期的计数显示在一个表中。

About_page

posted @ 2015-01-30 14:54  178mz  阅读(1780)  评论(0编辑  收藏  举报