【第四篇】ASP.NET MVC快速入门之完整示例(MVC5+EF6)
目录
【第一篇】ASP.NET MVC快速入门之数据库操作(MVC5+EF6)
【第二篇】ASP.NET MVC快速入门之数据注解(MVC5+EF6)
【第三篇】ASP.NET MVC快速入门之安全策略(MVC5+EF6)
【第四篇】ASP.NET MVC快速入门之完整示例(MVC5+EF6)
【番外篇】ASP.NET MVC快速入门之免费jQuery控件库(MVC5+EF6)
请关注三石的博客:http://cnblogs.com/sanshi
完善数据注解
到目前为止的表格页面效果:

我们需要更多的数据注解,来限制各个属性,以及提供显示用的名称(而不是英文字符串):
public class Student
{
public int ID { get; set; } [Display(Name = "姓名")]
[Required]
[StringLength(, MinimumLength = )]
public string Name { get; set; } [Display(Name = "性别")]
[Required]
[Range(, )]
public int Gender { get; set; } [Display(Name = "所学专业")]
[Required]
[StringLength()]
public string Major { get; set; } [Display(Name = "入学日期")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime EntranceDate { get; set; }
}
再次运行,表格页面效果:

完善性别的显示
表格页面-性别列显示为中文
这个比较简单,将原来的:
@Html.DisplayFor(modelItem => item.Gender)
修改为:
@if (item.Gender == 1)
{
@:男
} else
{
@:女
}
新建编辑页面-性别显示为下拉列表
原来的编辑页面:

性别字段的编辑框是通过如下方式生成的:
@Html.EditorFor(model => model.EntranceDate, new { htmlAttributes = new { @class = "form-control" } })
Html辅助方法EditorFor会查看模型属性的类型,自动生成对应的表单输入框。由于性别字段是整形,所以这里默认会生成一个数字输入框。
为了更加友好的显示,我们将性别改为下拉列表,并且仅允许用户从下拉项中选择。首先我们需要准备下拉列表选项的集合,并通过控制器传递给视图使用:
定义获取性别集合的函数,由于需要多个地方使用,所以提取成一个公共方法:
private List<SelectListItem> GetGenderList()
{
return new List<SelectListItem>() {
new SelectListItem
{
Text = "男",
Value = ""
},new SelectListItem
{
Text = "女",
Value = ""
}
};
}
通过ViewBag.GenderList传入视图:
// GET: Students/Edit/5
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Student student = db.Students.Find(id);
if (student == null)
{
return HttpNotFound();
} ViewBag.GenderList = GetGenderList();
return View(student);
}
视图中通过DropDownListFor强类型辅助方法,来显示下拉列表以及选中项:
@Html.DropDownListFor(model => model.Gender,
ViewBag.GenderList as IEnumerable<SelectListItem>, new { @class = "form-control" })

表单提交时的代码和之前一样,多了一个对GetGenderList的调用:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "ID,Name,Gender,Major,EntranceDate")] Student student)
{
if (ModelState.IsValid)
{
db.Entry(student).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
} ViewBag.GenderList = GetGenderList();
return View(student);
}
这一点非常重要,虽然正常的提交操作不会再次返回当前视图(RedirectToAction直接指定了页面跳转),但是在模型绑定失败时(尝试禁用JavaScript,姓名留空,然后提交表单),如果不重新设置ViewBag.GenderList参数就会出错:

表单检索
下面我们为表格页面增加一个搜索表单,用来对表格数据进行过滤。
先增加一些记录:

添加表单检索字段:
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<p>
所学专业: @Html.DropDownList("Major",
ViewBag.MajorList as IEnumerable<SelectListItem>, "全部")
姓名: @Html.TextBox("Name")
<input type="submit" value="检索" />
</p>
}
由于本示例比较简单,没有单独的表来存储所学专业,因此我们需要从用户表中检索,并存储到ViewBag.MajorList中传入视图:
private List<SelectListItem> GetMajorList()
{
var majors = db.Students.OrderBy(m => m.Major).Select(m => m.Major).Distinct(); var items = new List<SelectListItem>();
foreach(string major in majors)
{
items.Add(new SelectListItem {
Text = major,
Value = major
});
}
return items;
} // GET: Students
public ActionResult Index()
{
ViewBag.MajorList = GetMajorList();
return View(db.Students.ToList());
}
页面运行效果:

增加POST请求的处理方法:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(string Major, string Name)
{
var students = db.Students as IQueryable<Student>;
if (!String.IsNullOrEmpty(Name))
{
students = students.Where(m => m.Name.Contains(Name));
} if (!String.IsNullOrEmpty(Major))
{
students = students.Where(m => m.Major == Major);
} ViewBag.MajorList = GetMajorList();
return View(students.ToList());
}
此时的运行效果:

数据库分页
分页工具条
首先改造视图代码,增加分页工具条:
<div id="pagebar">
@for (var i = ; i < ViewBag.PageCount; i++)
{
if (i == ViewBag.PageIndex)
{
<span class="currentpagenumber">@(i + )</span>
}
else
{
<a class="pagenumber" href="javascript:;">@(i + )</a>
}
}
</div>
其中ViewBag.PageIndex和ViewBag.PageCount是由控制器传入的分页参数,我们需要这两个数据来构造分页链接,如果是当前分页就显示为文本,如果是其他页就显示为超链接,然后通过客户端JavaScript来注册点击事件。
EF的数据库分页
后台控制器代码:
private static readonly int PAGE_SIZE = ; private int GetPageCount(int recordCount)
{
int pageCount = recordCount / PAGE_SIZE;
if (recordCount % PAGE_SIZE != )
{
pageCount += ;
}
return pageCount;
} private List<Student> GetPagedDataSource(IQueryable<Student> students,
int pageIndex, int recordCount)
{
var pageCount = GetPageCount(recordCount);
if (pageIndex >= pageCount && pageCount >= )
{
pageIndex = pageCount - ;
} return students.OrderBy(m => m.Name)
.Skip(pageIndex * PAGE_SIZE)
.Take(PAGE_SIZE).ToList();
} // GET: Students
public ActionResult Index()
{
var students = db.Students as IQueryable<Student>;
var recordCount = students.Count();
var pageCount = GetPageCount(recordCount); ViewBag.PageIndex = ;
ViewBag.PageCount = pageCount; ViewBag.MajorList = GetMajorList();
return View(GetPagedDataSource(students, , recordCount));
}
EF为我们封装了大部分的细节,所以上面的数据库分页代码非常直观和容易理解:
students
.OrderBy(m => m.Name)
.Skip(pageIndex * PAGE_SIZE)
.Take(PAGE_SIZE).ToList()
完成一个典型的数据库分页需要如下几部:
1. OrderBy:指定排序列
2. Skip:跳过多少条记录
3. Take:返回的最大记录数
上面的OrderBy是必须指定的,否则就会报错:

分页SQL语句
完成上面的代码,分页效果已经出来了:

下面,我们使用第三方Express Profiler工具来检查EF生成的数据库分页SQL语句。
首先下载工具:
http://expressprofiler.codeplex.com/
打开Express Profiler,在Server文本框中输入(LocalDb)\MSSQLLocalDB,如果你使用的VS2013,这个字符串可能是:(LocalDb)\v11.0,点击绿色的启用按钮:

运行我们的示例,转到学生列表页面,然后清空Express Profiler中的全部显示,再点击第二页:

可以看到这里有3次SQL查询,这个和我们的心理预期是一样的:
1. 第一次SQL查询:总记录数
SELECT
[GroupBy1].[A1] AS [C1]
FROM ( SELECT
COUNT(1) AS [A1]
FROM [dbo].[Students] AS [Extent1]
) AS [GroupBy1]
go
对应的C#代码:
var students = db.Students as IQueryable<Student>;
var recordCount = students.Count();
2. 第二次SQL查询:所学专业集合(去除重复)
SELECT
[Distinct1].[Major] AS [Major]
FROM ( SELECT DISTINCT
[Extent1].[Major] AS [Major]
FROM [dbo].[Students] AS [Extent1]
) AS [Distinct1]
go
对应的C#代码:
var majors = db.Students.OrderBy(m => m.Major).Select(m => m.Major).Distinct();
3. 第三次SQL查询:分页数据
SELECT
[Extent1].[ID] AS [ID],
[Extent1].[Name] AS [Name],
[Extent1].[Gender] AS [Gender],
[Extent1].[Major] AS [Major],
[Extent1].[EntranceDate] AS [EntranceDate],
[Extent1].[Job] AS [Job]
FROM [dbo].[Students] AS [Extent1]
ORDER BY [Extent1].[Name] ASC
OFFSET 3 ROWS FETCH NEXT 3 ROWS ONLY
go
对应的C#代码:
return students.OrderBy(m => m.Name)
.Skip(pageIndex * PAGE_SIZE)
.Take(PAGE_SIZE).ToList();
这个查询顺序也和前面的EF代码的执行顺序一模一样,可以再回过头看下控制器Index方法。
同时处理表单检索和数据库分页
不过目前遇到点难题,我们希望实现如下两个功能:
1. 点击分页链接时会发出HTTP POST请求,在请求参数中带上表单检索值。
2. 表单检索时,在请求参数中带上当前所在的分页索引。
实现这两个功能才算完善,否则表单检索时如果丢失分页参数,就会回到第一页;而分页时如果丢失表单参数,就会清空表单输入框。
是不是开始怀念WebForms了,在WebForms中整个页面都被包含在一个表单中,因此回发时根本不需要考虑哪些参数后台需要。而MVC中这个就需要我们操心了,毕竟在灵活性的面前,便利性就会有所打折。
我们采取的办法是扩充前面的form标签,加入PageIndex隐藏字段,然后点击分页链接时提交表单即可:
@using (Html.BeginForm("Index", "Students", FormMethod.Post, new { id = "searchForm" }))
{
@Html.AntiForgeryToken()
<p>
所学专业: @Html.DropDownList("Major",
ViewBag.MajorList as IEnumerable<SelectListItem>, "全部")
姓名: @Html.TextBox("Name")
<input type="hidden" id="PageIndex" name="PageIndex" value="" />
<input type="button" id="searchButton" value="检索" />
</p>
}
注册JavaScript脚本来处理点击[检索]按钮和分页链接:
@section scripts {
<script>
function submitForm(pagenumber) {
pagenumber = parseInt(pagenumber, );
$('#PageIndex').val(pagenumber - );
$('#searchForm').submit();
}
$(function () {
$('#searchButton').click(function () {
submitForm($('#pagebar .currentpagenumber').text());
});
$('#pagebar .pagenumber').click(function () {
submitForm($(this).text());
});
});
</script>
}
现在看下效果,首先检索所学专业:

然后点击第二页,会发出一个POST请求:

可以看到本次请求,上面的用户输入和PageIndex都发送到了控制器处理方法:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(string Major, string Name, int PageIndex)
{
var students = db.Students as IQueryable<Student>;
if (!String.IsNullOrEmpty(Name))
{
students = students.Where(m => m.Name.Contains(Name));
} if (!String.IsNullOrEmpty(Major))
{
students = students.Where(m => m.Major == Major);
} var recordCount = students.Count();
var pageCount = GetPageCount(recordCount);
if (PageIndex >= pageCount && pageCount >= )
{
PageIndex = pageCount - ;
} students = students.OrderBy(m=>m.Name)
.Skip(PageIndex * PAGE_SIZE).Take(PAGE_SIZE); ViewBag.PageIndex = PageIndex;
ViewBag.PageCount = pageCount; ViewBag.MajorList = GetMajorList();
return View(students.ToList());
}
这里需要注意一点:先进行表单过滤,然后执行获取总记录数的查询,最后再获取分页数据。这个顺序不能变,因为表单过滤后总记录才能确定下来。
小结
本篇文章对示例进行了完善,首先是添加更多的数据注解,然后将显示的性别由数字改为字符串,对于编辑和新建页面,将性别渲染为下拉列表。然后为表格页面增加表单检索功能,可以根据[所学专业]和[姓名]对表格过滤,最后完成了数据库分页功能。
本系列文章至此已经完成了,下面我们简单总结下用到的关键词:路由引擎、控制器向视图传值、强类型辅助方法、模型绑定、数据注解、数据迁移、客户端验证、服务器端模型验证、模拟POST请求、表单身份验证、跨站请求伪造、过多提交攻击、表单检索、数据库分页。
【第四篇】ASP.NET MVC快速入门之完整示例(MVC5+EF6)的更多相关文章
- 【第三篇】ASP.NET MVC快速入门之安全策略(MVC5+EF6)
目录 [第一篇]ASP.NET MVC快速入门之数据库操作(MVC5+EF6) [第二篇]ASP.NET MVC快速入门之数据注解(MVC5+EF6) [第三篇]ASP.NET MVC快速入门之安全策 ...
- 【番外篇】ASP.NET MVC快速入门之免费jQuery控件库(MVC5+EF6)
目录 [第一篇]ASP.NET MVC快速入门之数据库操作(MVC5+EF6) [第二篇]ASP.NET MVC快速入门之数据注解(MVC5+EF6) [第三篇]ASP.NET MVC快速入门之安全策 ...
- 【第一篇】ASP.NET MVC快速入门之数据库操作(MVC5+EF6)
目录 [第一篇]ASP.NET MVC快速入门之数据库操作(MVC5+EF6) [第二篇]ASP.NET MVC快速入门之数据注解(MVC5+EF6) [第三篇]ASP.NET MVC快速入门之安全策 ...
- 【第二篇】ASP.NET MVC快速入门之数据注解(MVC5+EF6)
目录 [第一篇]ASP.NET MVC快速入门之数据库操作(MVC5+EF6) [第二篇]ASP.NET MVC快速入门之数据注解(MVC5+EF6) [第三篇]ASP.NET MVC快速入门之安全策 ...
- ASP.NET Core 快速入门(Razor Pages + Entity Framework Core)
引子 自从 2009 年开始在博客园写文章,这是目前我写的最长的一篇文章了. 前前后后,我总共花了 5 天的时间,每天超过 3 小时不间断写作和代码调试.总共有 8 篇文章,每篇 5~6 个小结,总截 ...
- ASP.NET MVC 5 入门指南汇总
经过前一段时间的翻译和编辑,我们陆续发出12篇ASP.NET MVC 5的入门文章.其中大部分翻译自ASP.NET MVC 5 官方教程,由于本系列文章言简意赅,篇幅适中,从一个web网站示例开始讲解 ...
- 《Entity Framework 6 Recipes》中文翻译系列 (20) -----第四章 ASP.NET MVC中使用实体框架之在MVC中构建一个CRUD示例
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第四章 ASP.NET MVC中使用实体框架 ASP.NET是一个免费的Web框架 ...
- ASP.NET MVC 5 入门摘要
翻译和编辑的第一阶段后,.我们已经发出12片ASP.NET MVC 5入门文章. 他们中的大多数来自翻译ASP.NET MVC 5 官方教程,因为本系列文章言简意赅,篇幅适中,从一个web站点演示样例 ...
- ASP.NET Core快速入门--学习笔记系列文章索引目录
课程链接:http://video.jessetalk.cn/course/explore 良心课程,大家一起来学习哈! 抓住国庆假期的尾巴完成了此系列课程的学习笔记输出! ASP.NET Core快 ...
随机推荐
- svn迁移gitlab,构建前端打包发布流程
前端资源迁移 目前公司的前端资源托管在svn服务器上,由于团队的逐渐扩大,svn的分支管控越来越不灵活,而且对于以后前端流程一体化的处理支持不是很好,因此决定在版本控制上转向git.git的好 ...
- Oracle架构设计01:表空间的管理维护规范
Oracle数据库的表空间管理可以说是非常简单和基础的一项维护工作,但是越简单的事情就越要制定统一的规范,这样数据库的各项管理工作才会愈加的简单高效. 那么接下来,问题来了.. Q1:当我们接手一个新 ...
- 初谈SQL Server逻辑读、物理读、预读
前言 本文涉及的内容均不是原创,是记录自己在学习IO.执行计划的过程中学习其他大牛的博客和心得并记录下来,之所以想写下来是为了记录自己在追溯的过程遇到的几个问题,并把这些问题弄清楚. 本章最后已贴出原 ...
- 在iOS中实现一个简单的画板App
在这个随笔中,我们要为iPhone实现一个简单的画板App. 首先需要指出的是,这个demo中使用QuarzCore进行绘画,而不是OpenGL.这两个都可以实现类似的功能,区别是OpenGL更快,但 ...
- deb包的安装及dpkg命令小结
DPKG commands There are two actions, they are dpkg-query and dpkg-deb. Install a package # sudo dpkg ...
- 跨平台运行 Rafy 首次部署记录
一直想在 Linux 上使用 MONO 试试运行 Rafy,最近因为业务需要,总算是真正地试验了一次.下面是本次部署记录的一些要点. Linux 这次部署,我是和两位同事一起来试验的.由于我们对 Li ...
- 使用java泛型设计通用方法
泛型是Java SE 1.5的新特性, 泛型的本质是参数化类型, 也就是说所操作的数据类型被指定为一个参数. 因此我们可以利用泛型和反射来设计一些通用方法. 现在有2张表, 一张user表和一张stu ...
- Struts2配置国际化资源
1. 国际化的目标 1). 如何配置国际化资源文件 I. Action 范围资源文件: 在Action类文件所在的路径建立名为 ActionName_language_country.properti ...
- Sublime Text 2 快捷操作
Sublime Text 2 包含了大量快捷操作,而且还很方便修改和追加自己喜欢的快捷键. 查看快捷键的方式也很简单: 点击菜单栏:Preferences->Key Bindings –Defa ...
- cmd命令汇总
一 cmd 命令 cmd命令大全(第一部分) winver---------检查Windows版本 wmimgmt.msc----打开windows管理体系结构(WMI) wupdmgr------ ...