目录

【第一篇】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)的更多相关文章

  1. 【第三篇】ASP.NET MVC快速入门之安全策略(MVC5+EF6)

    目录 [第一篇]ASP.NET MVC快速入门之数据库操作(MVC5+EF6) [第二篇]ASP.NET MVC快速入门之数据注解(MVC5+EF6) [第三篇]ASP.NET MVC快速入门之安全策 ...

  2. 【番外篇】ASP.NET MVC快速入门之免费jQuery控件库(MVC5+EF6)

    目录 [第一篇]ASP.NET MVC快速入门之数据库操作(MVC5+EF6) [第二篇]ASP.NET MVC快速入门之数据注解(MVC5+EF6) [第三篇]ASP.NET MVC快速入门之安全策 ...

  3. 【第一篇】ASP.NET MVC快速入门之数据库操作(MVC5+EF6)

    目录 [第一篇]ASP.NET MVC快速入门之数据库操作(MVC5+EF6) [第二篇]ASP.NET MVC快速入门之数据注解(MVC5+EF6) [第三篇]ASP.NET MVC快速入门之安全策 ...

  4. 【第二篇】ASP.NET MVC快速入门之数据注解(MVC5+EF6)

    目录 [第一篇]ASP.NET MVC快速入门之数据库操作(MVC5+EF6) [第二篇]ASP.NET MVC快速入门之数据注解(MVC5+EF6) [第三篇]ASP.NET MVC快速入门之安全策 ...

  5. ASP.NET Core 快速入门(Razor Pages + Entity Framework Core)

    引子 自从 2009 年开始在博客园写文章,这是目前我写的最长的一篇文章了. 前前后后,我总共花了 5 天的时间,每天超过 3 小时不间断写作和代码调试.总共有 8 篇文章,每篇 5~6 个小结,总截 ...

  6. ASP.NET MVC 5 入门指南汇总

    经过前一段时间的翻译和编辑,我们陆续发出12篇ASP.NET MVC 5的入门文章.其中大部分翻译自ASP.NET MVC 5 官方教程,由于本系列文章言简意赅,篇幅适中,从一个web网站示例开始讲解 ...

  7. 《Entity Framework 6 Recipes》中文翻译系列 (20) -----第四章 ASP.NET MVC中使用实体框架之在MVC中构建一个CRUD示例

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第四章  ASP.NET MVC中使用实体框架 ASP.NET是一个免费的Web框架 ...

  8. ASP.NET MVC 5 入门摘要

    翻译和编辑的第一阶段后,.我们已经发出12片ASP.NET MVC 5入门文章. 他们中的大多数来自翻译ASP.NET MVC 5 官方教程,因为本系列文章言简意赅,篇幅适中,从一个web站点演示样例 ...

  9. ASP.NET Core快速入门--学习笔记系列文章索引目录

    课程链接:http://video.jessetalk.cn/course/explore 良心课程,大家一起来学习哈! 抓住国庆假期的尾巴完成了此系列课程的学习笔记输出! ASP.NET Core快 ...

随机推荐

  1. svn迁移gitlab,构建前端打包发布流程

    前端资源迁移     目前公司的前端资源托管在svn服务器上,由于团队的逐渐扩大,svn的分支管控越来越不灵活,而且对于以后前端流程一体化的处理支持不是很好,因此决定在版本控制上转向git.git的好 ...

  2. Oracle架构设计01:表空间的管理维护规范

    Oracle数据库的表空间管理可以说是非常简单和基础的一项维护工作,但是越简单的事情就越要制定统一的规范,这样数据库的各项管理工作才会愈加的简单高效. 那么接下来,问题来了.. Q1:当我们接手一个新 ...

  3. 初谈SQL Server逻辑读、物理读、预读

    前言 本文涉及的内容均不是原创,是记录自己在学习IO.执行计划的过程中学习其他大牛的博客和心得并记录下来,之所以想写下来是为了记录自己在追溯的过程遇到的几个问题,并把这些问题弄清楚. 本章最后已贴出原 ...

  4. 在iOS中实现一个简单的画板App

    在这个随笔中,我们要为iPhone实现一个简单的画板App. 首先需要指出的是,这个demo中使用QuarzCore进行绘画,而不是OpenGL.这两个都可以实现类似的功能,区别是OpenGL更快,但 ...

  5. deb包的安装及dpkg命令小结

    DPKG commands There are two actions, they are dpkg-query and dpkg-deb. Install a package # sudo dpkg ...

  6. 跨平台运行 Rafy 首次部署记录

    一直想在 Linux 上使用 MONO 试试运行 Rafy,最近因为业务需要,总算是真正地试验了一次.下面是本次部署记录的一些要点. Linux 这次部署,我是和两位同事一起来试验的.由于我们对 Li ...

  7. 使用java泛型设计通用方法

    泛型是Java SE 1.5的新特性, 泛型的本质是参数化类型, 也就是说所操作的数据类型被指定为一个参数. 因此我们可以利用泛型和反射来设计一些通用方法. 现在有2张表, 一张user表和一张stu ...

  8. Struts2配置国际化资源

    1. 国际化的目标 1). 如何配置国际化资源文件 I. Action 范围资源文件: 在Action类文件所在的路径建立名为 ActionName_language_country.properti ...

  9. Sublime Text 2 快捷操作

    Sublime Text 2 包含了大量快捷操作,而且还很方便修改和追加自己喜欢的快捷键. 查看快捷键的方式也很简单: 点击菜单栏:Preferences->Key Bindings –Defa ...

  10. cmd命令汇总

    一  cmd 命令 cmd命令大全(第一部分) winver---------检查Windows版本 wmimgmt.msc----打开windows管理体系结构(WMI) wupdmgr------ ...