近期学习MVC5+EF6,找到了Microsoft的原文,一个非常棒的系列,Getting Started with Entity Framework 6 Code First using MVC 5,网址:http://www.asp.net/mvc/overview/getting-started/getting-started-with-ef-using-mvc/creating-an-entity-framework-data-model-for-an-asp-net-mvc-application。
这个系列的原文,可以上面网址找到。我也从网上找到了相关的译文。申明,这些译文,我不是原创,而是从网上找来的,为避免下次还要网上查询,现将这些译文整理放在上面。以后找时间将原文地址附上。
MVC5+EF6--3 排序、过滤和分页
Contoso University示例网站演示如何使用Entity Framework 5创建ASP.NET MVC 4应用程序。
Entity Framework有三种处理数据的方式: Database First, Model First, and Code First. 本指南使用代码优先。其它方式请查询资料。
示例程序是为Contoso University建立一个网站。功能包括:学生管理、课程创建、教师分配。 本系列指南逐步讲述如何实现这一网站程序。
本示例程序基于 ASP.NET MVC.如果使用 ASP.NET Web Forms model, 请查看 Model Binding and Web Forms系列指南和 ASP.NET Data Access Content Map.
如有问题,可在这些讨论区提问: ASP.NET Entity Framework forum, the Entity Framework and LINQ to Entities forum, or StackOverflow.com.
如有问题,可在这些讨论区提问: ASP.NET Entity Framework forum, the Entity Framework and LINQ to Entities forum, or StackOverflow.com.
上一指南中你已经实现了一系列页面完成对Student 实体的 CRUD 操作.本指南将完成对数据的排序、过滤和分页。 还会创建一个实现了分组的简单页面.
下图显示做完之后的效果,点击列名可对数据进行排序.
为了添加排序功能 需要改变Student 控制器的 Index方法 向其添加代码.
在 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());
}
代码接收URL中的sortOrder 参数. 通过 ASP.NET MVC 作为参数传递到行为方法。 参数值将为 "Name" 或"Date", 或后面跟着"_desc"。. 默认排序为升序 .
页面第一次加载时没有参数,因此switch 执行到最后默认 按 LastName的升序排列. 当用户点击了列标题之后, sortOrder 值将被传递到方法中.
ViewBag 变量记录了下次排序应该传递的排序参数:
ViewBag.NameSortParm = String.IsNullOrEmpty(sortOrder) ? "Name_desc" : "";
ViewBag.DateSortParm = sortOrder == "Date" ? "Date_desc" : "Date";
这是交替语句. 第一句的意思是 sortOrder 参数为NULL或者空值,ViewBag.NameSortParm 应设为 "name_desc"; 否则,将其设为空值. 这两条语句使得当前排序和当前排序连接的关系如下:
|
当前排序 |
Last Name 链接 |
Date 链接 |
|
Last Name ascending |
descending |
ascending |
|
Last Name descending |
ascending |
ascending |
|
Date ascending |
ascending |
descending |
|
Date descending |
ascending |
ascending |
方法使用 LINQ to Entities 指明排序的列. 在switch 之前创建了一个 IQueryable 变量, 并在 switch 语句中修改此变量, 然后调用 ToList 方法. 在创建和修改 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 属性中的值设置链接中的排序参数.
运行并测试排序是否成功.
添加一个文本框和一个按钮用于查询。
在 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 参数,同时添加了where过滤条件. where 语句只有在searchString 有值的时候执行.
注意:很多情况下在Entity Framework实体集或在内存集合中作为扩展方法调用同样的方法,通常结果是一样的,但有时结果会不同。比如,.NET Framework中如果给Contains方法传递一个空字符串作为参数将返回所有行,但Entity Framework provider for SQL Server Compact 4.0 返回零行。因此本例的代码(把Where语句放在If语句内)确保在SQL Server所有版本中得到同样的结果。而且.NET Framework 的Contains方法是大小写敏感的,但Entity Framework provider for SQL Server 默认大小写不敏感。因此,调用ToUpper 方法确保在你使用repository修改了代码之后得到的结果不会有变化,repository将返回一个IEnumerable集合而不是IQueryable对象。(当在IEnumerable 调用Contains方法,由 .NET Framework来处理;当在IQueryable 调用Contains时,是由 database provider来处理的。)
在 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 是很好用的分页排序包之一,这里使用只是为了示例并不是说 PagedList.Mvc就是最好的。
NuGet PagedList.Mvc 自动安装其依赖的 PagedList包.PagedList包安装 PagedList 集合类型和相应的针对IQueryable 或IEnumerable 的扩展方法 . 扩展方法根据IQueryable 或IEnumerable生成一个 PagedList 集合 , PagedList collection 提供分页相关的属性和方法。PagedList.Mvc安装一个 paging helper 用来显示分页按钮.
从工具菜单, 选择Library Package Manager -> Manage NuGet Packages.
在 Manage NuGet Packages 对话框, 选择 Online 然后在搜索框输入 "paged" .浏览到PagedList.Mvc 包, 点击安装.
在选择项目对话框选中项目, 点击 OK.
在 Controllers\StudentController.cs, 添加 using 语句引入 PagedList 命名空间:
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 变量的值是要显示页的页码.
ViewBag 属性记录当前排序, 为了保障分页数据一致,分页链接中需要包含排序信息:
ViewBag.CurrentSort = sortOrder;
另一个属性 ViewBag.CurrentFilter, 存储当前过滤参数. 页码链接必须包含此参数以保证分页数据对过滤有效, 当页面重新加载也能重置搜索框内的信息.如果过滤字符串在分页时发生变化, 页面跳转到第一页, 因为新的过滤导致数据不一样了. 当搜索框内容改变而且点击了提交按钮之后过滤才会改变. 在这种情况下, searchString 参数的值不再是 null.
if (searchString != null)
page = 1;
else
searchString = currentFilter;
方法最后, IQueryable的ToPagedList 扩展方法将 student 查询结果转为支持分页的集合中的一页. 这一页内容将传递给视图:
int pageSize = 3;
int pageNumber = (page ?? 1);
return View(students.ToPagedList(pageNumber, pageSize));
ToPagedList 方法需要一个页码参数. (page ?? 1) 的意思是如果 page 不为null则返回, 如果 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 helper .
此代码覆盖了之前的,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 提交使用 POST, 也就是说提交内容包含在 HTTP 消息中而不是 URL中. 如果指明 HTTP GET, 参数将通过UR传递, 添加书签之后 URL才有效. W3C guidelines for the use of 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
如果没有内容, 将显示"Page 0 of 0". (这种情况 page number大于 page count,因为 Model.PageNumber 是1,Model.PageCount 是0.)
页码按钮通过 PagedListPager helper显示:
@Html.PagedListPager( Model, page => Url.Action("Index", new { page }) )
PagedListPager helper 提供很多自定义功能,详细信息请查看TroyGoode / PagedList .
运行页面.
测试分页和过滤功能是否有效.
About将显示每个登记日期有多少学生进行登记 . 这需要使用分组. 通过以下步骤可实现:
Home 控制器的 About 方法.About 视图.新建 ViewModels 文件夹. 在此文件夹添加 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>
运行程序查看结果.
原文:http://www.cnblogs.com/dlhjwang/p/4420815.html