EF6与mvc5系列(3):在MVC应用程序中使用EF进行排序,过滤和分页

上节中,我们实现了基本增删查改功能,本节中要在Student的Index页面添加排序,分页和过滤功能,同时创建一个简单的分组页面。

在Student的Index页面添加列排序链接

为了在Index页面中实现排序。修改Index方法中的代码。

在Index方法中添加排序功能

修改Student控制器中的Index方法,在Index视图中添加代码。

// GET: /Student/
        public ActionResult Index(string sortOrder)
        {
            ViewBag.NameSort = string.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
            ViewBag.DateSort=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());
        }

上述方法中,接收到一个来自URL的sortOrder查询字符串参数,查询字符串的值是由MVC提供,作为一个参数传递给action。参数值是“Name”或者“Date”,然后后面随意跟随_desc用于说明降序排列,默认是正序。

第一次请求Index页面,是没有查询字符串的,页面会呈现出按照Last字段正序查询出的数据。当用户点击列标题超链接的时候,会传递sortOrder的值进行查询。

Index方法使用了LINQ to Entities 指定要排序的列。在switch语句之前创建IQueryable 变量,并在switch语句中修改其值,最后调用ToList()方法。当你创建并修改IQueryable变量的时候,不向数据库中发送查询请求。当你将IQueryable对象通过像ToList这样的方法,转换为集合的时候才会执行查询。有关更多动态LINQ,请查看:Dynamic LINQ.

在Index视图页添加列标题超链接

对Index视图代码做如下修改:

<table class="table">
    <tr>
        <th>
            @Html.ActionLink("LastName", "Index", new { sortOrder=ViewBag.NameSort})
        </th>
        <th>
            @*@Html.DisplayNameFor(model => model.FirstMidName)*@
            FirstMidName
        </th>
        <th>
            @Html.ActionLink("Enrollment Date", "Index", new { sortOrder=ViewBag.DateSort})
        </th>
        <th></th>
    </tr>
在Index页面添加搜索框

接下来实现页面的搜索功能

在Index方法中添加过滤功能

修改Index方法如下:

 // GET: /Student/
        public ActionResult Index(string sortOrder,string searchStr)
        {
            ViewBag.NameSort = string.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
            ViewBag.DateSort=sortOrder=="Date"?"date_desc":"Date";
            var students = from s in db.Students select s;
            if (!string.IsNullOrEmpty(searchStr))
            {
                students = students.Where(s=>s.LastName.Contains(searchStr)||s.FirstMidName.Contains(searchStr));
            }
            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方法中添加searchStr参数,此参数来自页面的输入框中写入的值,在where字句中添加LINQ筛选出符合条件的实体。

注意:在很多情况下,可以对EF实体集,或者作为内存中集合的扩展方法调用相同的方法,其作用结果相同。但是有些情况下是不同的。

例如:.NET Framework实现Contains方法的时候,如果传入空字符串会返回数据库中所有行。但是对于SQL Server Compact 4实体框架提供程序,如果输入空字符串不会返回任何行。因此才会将上述中的where字句放在if中以确保不同情况下都能显示数据。同时,.NET Framework实现Contains方法的时候默认是区分大小写的。但是SQL Server Compact 4实体框架提供程序默认不区分大小写。为了避免这种情况,我们可以使用ToUpper方法避免这种情况。稍后的代码中,我们的结果集会返回一个IEnumerable集合,而不是一个IQueryable对象。对IEnumerable集合调用Contains方法的时候,是由.NET Framework实现。而对IQueryable调用Contains方法的时候,是由数据库提供程序实现的。

不同的数据库应用程序对NULL处理也不同。例如,有时候一个where中包含table.Column != 0时,不会返回带有NULL的数据行。更多信息请查看: Incorrect handling of null variables in 'where' clause.

Index视图中添加搜索框

如下:

<p>
    @Html.ActionLink("Create New", "Create")
</p>
@using(Html.BeginForm())
{
    <P>
        Find by Name:@Html.TextBox("searchStr")
        <input type="submit" value="Search"/>
    </P>
}
<table class="table">

注意:.NET Framework实现了Contains方法,当你传入的字符串为空时,返回所有行,但是SQL Server契约4.0的EF提供程序对于这种情况会返回0行数据。就是考虑到这种情况所以将where声明放在If中。.NET Framework对于Contains方法是区分大小写的,但是EF sql server提供程序对于Contains方法是不区分大小写的。对于IEnumerable
集合调用Contains方法会由.NET Framework实现。当对IQueryable对象调用Contains方法,会由数据库提供程序实现。

空处理对于不同的数据库提供程序或者 IQueryable对象和IEnumerable集合也是不同的。详情查看: Incorrect handling of null variables in 'where' clause.

添加分页

接下来在Index页面添加分页,我们使用PagedList.Mvc进行分页。我们仅仅在这个例子中使用它,不推荐在其他地方使用其进行分页。

安装PagedList.MVC NuGet包。

工具->库程序包管理器(NuGet程序包管理器)—>程序包管理器控制台->Install-Package PagedList.Mvc

生成项目

在Index方法中添加分页

引入命名空间

using PagedList;
 public ActionResult Index(string sortOrder,string searchStr,int? page,string currentFilter)
        {
            ViewBag.CurrentSort = sortOrder;
            //如果是sortOrder为空,返回name_desc,否则返回空字符串。
            ViewBag.NameSort = string.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
            ViewBag.DateSort=sortOrder=="Date"?"date_desc":"Date";
            if (searchStr!=null)
            {
                page = 1;
            }
            else
            {
                searchStr = currentFilter;
            }
            ViewBag.CurrentFilter = searchStr;
            var students = from s in db.Students select s;
            if (!string.IsNullOrEmpty(searchStr))
            {
                students = students.Where(s=>s.LastName.Contains(searchStr)||s.FirstMidName.Contains(searchStr));
            }
            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 = 2;
            int pageNum = (page ?? 1);
            return View(students.ToPagedList(pageNum,pageSize));
        }
 1 @*@model IEnumerable<ContosoUniversity.Models.Student>*@
 2 @model PagedList.IPagedList<ContosoUniversity.Models.Student>
 3 @using PagedList.Mvc
 4 
 5 
 6 @{
 7     ViewBag.Title = "Student";
 8 }
 9 
10 <h2>Student</h2>
11 
12 <p>
13     @Html.ActionLink("Create New", "Create")
14 </p>
15 @using(Html.BeginForm("Index","Student",FormMethod.Get))
16 {
17     <P>
18         Find by Name:@Html.TextBox("searchStr", ViewBag.CurrentFilter as string)
19         <input type="submit" value="Search"/>
20     </P>
21 }
22 <table class="table">
23     <tr>
24         <th>
25             @Html.ActionLink("LastName", "Index", new { sortOrder = ViewBag.NameSort, currentFilter = ViewBag.CurrentFilter })
26         </th>
27         <th>
28             @*@Html.DisplayNameFor(model => model.FirstMidName)*@
29             FirstMidName
30         </th>
31         <th>
32             @Html.ActionLink("Enrollment Date", "Index", new { sortOrder = ViewBag.DateSort, currentFilter = ViewBag.CurrentFilter })
33         </th>
34         <th></th>
35     </tr>
36 
37 @foreach (var item in Model) {
38     <tr>
39         <td>
40             @Html.DisplayFor(modelItem => item.LastName)
41         </td>
42         <td>
43             @Html.DisplayFor(modelItem => item.FirstMidName)
44         </td>
45         <td>
46             @Html.DisplayFor(modelItem => item.EnrollmentDate)
47         </td>
48         <td>
49             @Html.ActionLink("Edit", "Edit", new { id=item.ID }) |
50             @Html.ActionLink("Details", "Details", new { id=item.ID }) |
51             @Html.ActionLink("Delete", "Delete", new { id=item.ID })
52         </td>
53     </tr>
54 }
55 
56 </table>
57 <br />
58 第 @(Model.PageCount<Model.PageNumber?0:Model.PageNumber) 页&nbsp;&nbsp;;共 @Model.PageCount 页
59 @Html.PagedListPager(Model, page => Url.Action("Index", new { page, sortOrder = ViewBag.CurrentSort, currentFilter = ViewBag.CurrentFilter }))

页面首次加载或者用户未点击分页或者分类链接,所有的参数为null,如果点击分页链接,将看到page变量中包含了页码。

ViewBag属性提供给页面一个当前排序,因为分页时也要保持现有的排序,

ViewBag.CurrentSort = sortOrder;

另一个属性ViewBag.CurrentFilter,向页面提供当前过滤字符串,这样确保分页的时候也保持当前过滤条件。如果分页时查找条件改变,页面就重置为1,因为新的过滤条件查询结果与之前不同。

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

在方法的最后,students的IQueryable扩展方法:ToPagedList,将查询结果转为一个分页的学生集合,并传递到视图页。

int pageSize = 3;
int pageNumber = (page ?? 1);
return View(students.ToPagedList(pageNumber, pageSize));

修改页面的@model,将List对象改为PageList对象。

using声明能够访问MVC的辅助方法。

BeginForm默认提交数据方式为POST,这表示在http消息体中传入的查询字符串参数而不是在URL中传入。当指定HTTP GET,表单数据会以URL作为查询字符串传入这样会使用户看到URL参数,在W3C guidelines for the use of HTTP GET中提到,当Action不是update时推荐使用GET。

使用当前查询字符串初始化文本框。当点击新页面时可以看到当前查询字符串。


        Find by Name:@Html.TextBox("searchStr", ViewBag.CurrentFilter as string)

列标题连接使用查询字符串把关键字传递给控制器,这样用户就可以对查询出的结果进行排序。

@Html.ActionLink("LastName", "Index", new { sortOrder = ViewBag.NameSort, currentFilter = ViewBag.CurrentFilter })

显示当前页和总页数

Page @(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber) of @Model.PageCount

使用PagedListPager的辅助方法显示分页按钮。

@Html.PagedListPager( Model, page => Url.Action("Index", new { page }) )
创建About页面显示学生统计信息

在这个页面中,将要显示每学期有多少学生入学,这用到了分组和简单的分组计算。接下来将要做:

  • 创建需要传递给视图用的模型类
  • 修改HomeController中的About方法
  • 修改About视图页面
创建视图模型

在项目中添加ViewModels文件夹,在此文件夹中添加类EnrollmentDateGroup.cs。代码如下:

 1 using System;
 2 using System.ComponentModel.DataAnnotations;
 3 
 4 namespace ContosoUniversity.ViewModels
 5 {
 6     public class EnrollmentDateGroup
 7     {
 8         [DataType(DataType.Date)]
 9         public DateTime? EnrollmentDate { get; set; }
10         public int StudentCount { get; set; }
11     }
12 }
修改HomeController

在Home控制器中添加引用:

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);
        }
修改About视图
@model IEnumerable< ContosoUniversity.ViewModels.EnrollmentDateGroup>

@{
    ViewBag.Title = "学生统计";
}
<h2>学生统计</h2>
<table class="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>

<p>Use this area to provide additional information.</p>

运行页面,ok。本节完!

 

转载于:https://www.cnblogs.com/peaceOfMind/p/5328725.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值