您的当前位置:首页正文

mvc

来源:好兔宠物网


入门基础篇

EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程

本书定价:9元

EntLib.com 团队感谢您的支持!

您的支持是EntLib.com 团队不断发展的动力,我们会提供更好的产品和服务。

EntLib.com团队

禁止任何个人、公司和网站,将本电子书分享下载! 这是EntLib.com团队成长的需要,感谢您的支持! 授权用户信息:

姓名:郝斌(744827451@qq.com) 订单编号:480830580841544

http://www.EntLib.com

2

EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程

关于EntLib.com团队

EntLib.com团队专注于ASP.NET/C#/MVC/SQL Server 开源电子商务系统和iPhone/iPad、Windows Phone和Android移动平台开发技术。

欢迎电商和互联网同行分享和交流心得,让中国软件开发人员也为电子商务行业的繁荣奉献一点点力量。

EntLib 官方微博(新浪):EntLib开源电子商务平台(请关注我们)

EntLib电子商务系统 – QQ 群:255261329 交流ASP.NET、MVC、SQL Server、电子商务系统。

业务合作请联系:

QQ:1665600601 (请注明业务合作)

http://www.EntLib.com

3

EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程

目 录

本书定价:9元 ................................................................................................................... 2 关于ENTLIB.COM团队 ........................................................................................................ 3 目 录 .................................................................................................................................. 4 第一章 ASP.NET MVC 4 入门简介 ...................................................................................... 6 安装ASP.NET MVC 4 ............................................................................................................. 6 部署ASP.NET MVC 4 应用程序 ............................................................................................ 6 创建 ASP.NET MVC 4 应用程序 ........................................................................................... 6 MVC 应用程序的结构 ....................................................................................................... 12 ASP.NET MVC 和约定 ......................................................................................................... 15 第二章 控制器(CONTROLLER) ...................................................................................... 16 HOME CONTROLLER控制器 .................................................................................................... 16 编写第一个控制器 ............................................................................................................. 19 创建新的控制器................................................................................................................. 19 编写ACTION方法 ................................................................................................................ 21 控制器方法中的参数 ......................................................................................................... 23 第三章 视图(VIEWS) .................................................................................................... 25 分析HOME视图 ................................................................................................................. 25 添加视图 ............................................................................................................................ 26 修改视图和布局页面 ......................................................................................................... 30 从控制器传递数据给视图 ................................................................................................. 33 第四章 模型(MODEL) ................................................................................................... 38 添加模型类 ........................................................................................................................ 38 创建连接字符串和SQL SERVER数据库 .............................................................................. 40 第五章 从控制器中访问模型数据 .................................................................................... 41 创建MOVIESCONTROLLER控制器 .......................................................................................... 41 创建一条新的MOVIE记录 ................................................................................................. 43

http://www.EntLib.com

4

EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程

检查自动生成的代码 ......................................................................................................... 44 强类型模型和@MODEL关键字 ........................................................................................... 45 检查SQL SERVER数据库 ..................................................................................................... 47 检查EDIT 方法和EDIT 视图 ............................................................................................... 49 处理POST请求 .................................................................................................................. 54 添加SEARCH方法和SEARCH视图 ........................................................................................ 56 显示SEARCHINDEX表单 ....................................................................................................... 56 添加按类型(GENRE)搜索 ............................................................................................... 63 SEARCHINDEX视图支持按类型查询 ..................................................................................... 64 第六章 在MOVIE模型和表中添加一个新的字段 ........................................................... 66 为模型更新设置CODE FIRST 迁移 ....................................................................................... 66 在MOVIE模型中新增RATING属性 ..................................................................................... 72 第七章 在模型类中添加验证逻辑 .................................................................................... 80 保持DRY(DON’T REPEAT YOURSELF) .................................................................................. 80 在MOVIE模型中添加验证规则 .......................................................................................... 80 用户界面验证错误信息 ..................................................................................................... 83 CREATE视图和CREATE动作方法中的验证代码 ................................................................... 84 为MOVIE模型添加格式化属性 .......................................................................................... 87 第八章 检查DETAILS和DELETE方法 ............................................................................... 89 检查DETAILS和DELETE方法 ................................................................................................ 89 总 结 .................................................................................................................................. 91

http://www.EntLib.com

5

EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程

第一章 ASP.NET MVC 4 入门简介

安装ASP.NET MVC 4

ASP.NET MVC 4 开发包支持Visual Studio 2010 和 Visual Studio 2012,包括对应的免费Express 版本。MVC 4 已经包含在Visual Studio 2012中了,如果安装了Visual Studio 2012,就不必安装了。如果使用Visual Studio 2010,可以通过Web Platform Installer (http://www.microsoft.com/web/gallery/install.aspx?appid=MVC4VS2010)来安装。

部署ASP.NET MVC 4 应用程序

如果在Server上安装好ASP.NET MVC 4,MVC 运行时程序集会安装到Global Assembly Cache(GAC)中,表示这些程序集对所有运行在该Server上的站点都可以使用。 如果在Server上没有安装ASP.NET MVC 4,在ASP.NET MVC 4 之前,可以使用bin 发布,也就是在Visual Studio 开发工具中手动设置程序集复制到本地(Copy Local)。在ASP.NET MVC 4 之后,所有的程序集通过NuGet引用包含进来了。这样,所有需要的程序集都自动添加到项目中的bin目录,任何MVC 4 应用程序都支持bin 发布。

创建 ASP.NET MVC 4 应用程序

本教程所有示例截图采用Visual Studio 2012开发工具,和使用Visual Studio 2010 开发工具开发ASP.NET MVC 4 应用程序界面基本一致。 创建一个新的MVC项目:

http://www.EntLib.com

6

EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程

1. 选择文件 > 新建 > 项目菜单项,打开新建项目窗口。

选择ASP.NET MVC 4 Web Application,将应用程序命名为MvcMovie,点击OK按钮。 接下来,将弹出一个MVC项目模板窗口,下面是一些预先安装好的项目模板。

http://www.EntLib.com

7

EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程

http://www.EntLib.com

8

EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程

Internet 应用程序模板 – 包含MVC web应用程序的开始部分,这样可以在创建好应用程序之后,就可以马上运行应用程序了。这个模板包含了基于ASP.NET Membership system的一些基本账号管理功能。

Intranet应用程序模板 – 在ASP.NET MVC 3 Tools Update中增加了这个模板,和Internet Application模板相似,但是账号管理功能是基于Windows账号,而不是ASP.NET Membership system。

基本(Basic)模板 – 这个模板非常小,包含有基本的文件夹,CSS和MVC应用程序基础架构。运行该模板创建的项目将出现错误。基本模板适用于有经验的MVC开发人员,开发

http://www.EntLib.com

9

EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程

人员希望按照自己的需要设置和配置应用程序。

空(Empty)模板 – 基本(Basic)模板之前成为空模板,但是开发人员抱怨并不是真正的空模板。在ASP.NET MVC 4 中,之前的空模板更名为基本模板,新的空模板则是空的,它包含有程序集和基本的目录结构。

移动应用程序模板(Mobile Application template) - 移动应用程序模板配置为jQuery Mobile,用来创建面向移动终端的网站。它包含有移动可视化主题、触摸优化用户界面和支持Ajax导航。

Web API 模板 – ASP.NET Web API是用来创建HTTP services的框架。Web API模板和Internet应用程序模板比较相似,但为Web API开发进行了简化。例如,它没有用户账号管理功能,因为Web API 账号管理通常和标准MVC账号管理不同。在其他MVC项目模板中也有Web API功能,甚至在非MVC项目类型中。 视图引擎

在新ASP.NET MVC 4 项目对话框中另外一个选项是视图引擎下拉列表。视图引擎提供了在MVC应用程序中不同模板语言来生成HTML标记。在MVC 3 之前,视图引擎仅有内置的ASPX。在MVC 3 中添加了新的选项:Razor 视图引擎。

测试

所有内置项目模板都提供选项创建一个单元测试项目,并提供了示例单元测试。

http://www.EntLib.com

10

EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程

如果不勾选创建单元测试项目复选框,则项目不会创建任何单元测试。

在选择创建单元测试项目复选框后,可以看到如下选择:  第一个是单元测试项目的名称,可以修改;

 第二个是选择测试框架;默认只有一个测试框架选项,如果我们按照其他单元测试框架,

如xUnit、NUnit、MbUnit等等,就可以在下拉列表中看到了。 按照下图的设置创建新的ASP.NET MVC 4项目,点击OK按钮。

http://www.EntLib.com

11

EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程

这样将创建一个包含两个项目的solution(解决方案),一个是web应用程序,另外一个单元测试项目。

MVC 应用程序的结构

在我们使用Visual Studio 2012创建一个新的ASP.NET MVC应用程序时,它将自动添加一些文件和目录到项目中,如下图所示。通过Internet应用程序模板创建的ASP.NET MVC项目有9个一级目录。

http://www.EntLib.com

12

EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程

默认一级目录结构:

/Controllers – 存放负责处理URL请求的控制器类; /Models – 存放表示和操纵数据以及业务对象的类; /Views – 存放负责呈现输出内容的UI模板文件; /Scripts – 存放JavaScript 类库文件和脚本文件.js; /Images – 存放网站中使用到的图像文件;

/Content – 存放CSS和其他非Scripts和图像的网站内容; /Filters – 存放过滤器代码,过滤器是一个高级特性; /App_Data – 存放可读写数据文件;

/App_Start – 存放功能配置代码,如Routing、Bundling、Web API等等。

这个只是ASP.NET MVC 的默认项目结构,开发人员可以根据实际需要进行调整。如在大

http://www.EntLib.com

13

EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程

型应用程序中,一般使用多个项目,如将数据模型类存放在一个独立的Class Library项目中,让项目更易于管理。然而,默认的项目结构提供了一个很好的默认目录约定,使应用程序的关注点很清晰。

除了默认的目录结构之外,Visual Studio 也创建了一些默认的文件。如在/Controllers目录下,有两个控制器(Controller)类,分别为HomeController和AccountController。在/Views目录下,有三个子目录,分别为/Account、/Home和/Shared,以及一些模板文件。还有/Content和/Scripts目录,Site.css文件用来设置站点的HTML样式,JavaScript类库则让应用程序支持jQuery应用。在MvcMusticStore.Tests项目中,包含了对Controller类的单元测试。

如有兴趣,你可以通过Visual Studio 2012 运行一下我们刚刚创建的MVC应用程序,效果如下所示,都是模板默认创建的功能和页面:

http://www.EntLib.com

14

EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程

ASP.NET MVC 和约定

默认情况下,ASP.NET MVC应用程序非常依赖于约定。如ASP.NET MVC在

\\Views\\Controller-Name目录下查找视图模板文件。MVC是围绕一些默认约定来设计的,在需要的时候可以覆盖,这个概念通常称为约定优于配置(convention over configuration)。

ASP.NET MVC 依赖于使应用程序工作的三个核心目录:  Controllers  Models  Views

我们不必在web.config文件中设置这些目录,按照约定这些目录应该存在于项目中。ASP.NET MVC的约定是非常好直接的,下面是应用程序结构所期望的约定:  每一个Controller的类文件,以Controller结束,如ProductController、

HomeController等等,并且存放在Controllers目录中;  应用程序中有一个唯一的Views目录,存放所有的视图文件;

 控制器使用的视图存放在Views主目录下的控制器类名(去除Controller后缀)下的

子目录中。如ProductController控制器的视图存放在/Views/Product目录中。 所有可重用的UI元素存放在相似的目录结构中,但是在/Views/Shared目录中。

http://www.EntLib.com

15

EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程

第二章 控制器(Controller)

在ASP.NET MVC中,每一个Web请求都路由到控制器中的一个方法或操作上,控制器负责解释这些请求,并操作数据模型,然后选择一个视图,响应用户的请求。

Home Controller控制器

在开始编写代码之前,我们打开之前创建的项目中Home Controller控制器。在使用Internet应用程序模板创建的MVC项目中包含了两个控制器类:  HomeController – 负责网站的首页,about页面和contact页面;  AccountController – 负责账号相关的情况,如login和账号注册等等; 在Visual Studio 项目中,展开/Controllers目录,打开HomeController.cs文件。

http://www.EntLib.com

16

EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程

HomeController 继承Controller 基类,其中Index方法负责处理浏览网站首页的请求。 下面我们修改Index方法的代码,更改ViewBag.Message 赋值的字符串,更改后代码如下所示:

using System;

using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc;

namespace MvcMusicStore.Controllers {

public class HomeController : Controller {

public ActionResult Index() {

ViewBag.Message = \"欢迎光临 EntLib.com 开源电子商务平台!\";

return View(); }

public ActionResult About() {

ViewBag.Message = \"你的应用程序说明页。\";

return View(); }

public ActionResult Contact() {

ViewBag.Message = \"你的联系方式页。\";

return View(); } } }

点击 F5键,或者使用工具条上的运行按钮,Visual Studio将编译应用程序,并在IIS Express中启动站点。

http://www.EntLib.com

17

EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程

MVC 应用程序在浏览器中运行效果如下所示:

Visual Studio 2012包含了IIS Express,是IIS的一个本地开发版本,可在一个随机可用的端口号运行我们的网站。

Visual Studio 2010和之前版本使用Visual Studio Development Server(也称为

http://www.EntLib.com

18

EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程

Cassini),而不是IIS Express。尽管Development Server和IIS 相似,但是IIS 7.5 Express实际上IIS为开发目的进行优化的版本。

我们也可以在项目属性窗口的Web标签页查看或调整开发服务器的设置。

编写第一个控制器 创建新的控制器

右击Solution Explorer中的Controllers文件夹,选择添加>控制器…菜单项。

http://www.EntLib.com

19

EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程

在弹出的添加控制器窗口中,控制器名称设置为HelloWorldController,模板选择空MVC控制器,如下图所示。

点击添加按钮。在解决方案资源管理器中添加一个新的文件HelloWorldController.cs,该文件已经显示在IDE中。

http://www.EntLib.com

20

EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程

编写Action方法

更新HelloWorldController.cs文件的代码如下所示。

using System;

using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc;

namespace MvcMovie.Controllers {

public class HelloWorldController : Controller { //

// GET: /HelloWorld/

public string Index() {

return \"This is my default action...\"; }

//

// GET: /HelloWorld/Welcome/

public string Welcome() {

return \"This is the Welcome action method...\"; }

http://www.EntLib.com

21

EntLib.com 团队编写 } }

一步一步学习ASP.NET MVC 编程

上述控制器的方法都返回一个HTML字符串,其中第一个方法是Index。我们现在运行应用程序,在浏览器中浏览/HelloWorld地址。浏览器中运行效果如下所示。该方法直接返回一个字符串。

ASP.NET MVC 根据请求的URL地址调用不同的控制器类和不同的动作方法(Action Method)。ASP.NET MVC 使用的默认URL路由逻辑如下所示: /[Controller]/[ActionName]/[Parameters]

URL 路径的第一部分决定了执行的控制器类,因此/HelloWorld映射到

HelloWorldController类。URL 路径的第二部分决定了要执行类的动作方法,因此/HelloWorld/Index 将执行HelloWorldController类的Index方法。前面我们浏览/HelloWorld地址,默认调用Index方法。这是因为如果没有明确指定动作方法,将调用默认的Index方法。

浏览/HelloWorld/Welcome 路径,调用Welcome方法,并返回字符串This is the Welcome action method...。默认MVC映射为

/[Controller]/[ActionName]/[Parameters]。针对这个URL地址,控制器为HelloWorld,动作方法为Welcome,还没有使用URL地址的[Parameters]部分。

http://www.EntLib.com

22

EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程

从上面的工作中,我们可得出如下结论:

1. 浏览/HelloWorld/Welcome将执行HelloWorldController类的Welcome方法,不必任何额外的配置。

2. HelloWorldController控制器类继承System.Web.Mvc.Controller基类;

3. 我们使用控制器在浏览器中显示一段文本,没有使用模型(model)和视图(view)。尽管模型和视图在有实际用途的ASP.NET MVC 应用程序中是必不可少的,但控制器是核心部分。每一个Web请求都需要经过控制器,然而某些请求则可不必使用模型和视图。

控制器方法中的参数

现在我们修改前面的代码,从URL地址传入一些参数信息给控制器,如

/HelloWorld/Welcome?name=Rickie&numtimes=5。修改Welcome方法,添加两个参数。注意代码中使用了C#可选参数(optional-parameter)功能,表示如果没有传入numTimes参数,该参数将默认为1。

//

// GET: /HelloWorld/Welcome/

public string Welcome(string name, int numTimes = 1) {

return HttpUtility.HtmlEncode(\"Hello \" + name + \NumTimes is: \" + numTimes); }

再次运行应用程序,浏览URL地址/HelloWorld/Welcome?name=Rickie&numtimes=5。

http://www.EntLib.com

23

EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程

当然,我们也可以对URL地址中name和numtimes输入不同的参数值。ASP.NET MVC 模型绑定系统自动映射URL地址中查询字符串的命名参数给控制器方法中的参数。

控制器动作方法就好像是Web浏览器直接调用控制器中的方法。不过,在一般应用中,控制器通过URL调用,然后执行动作方法,并返回一个视图。

http://www.EntLib.com

24

EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程

第三章 视图(Views)

视图是应用程序向用户呈现的用户界面,是应用程序的可视化部分。视图引用模型对象,并转换模型数据为良好格式和布局,展示给用户。

分析Home视图

前面我们谈到过,MVC项目中/Views/Home目录下,自动创建了相应的视图文件。按照约定,每一个控制器都有一个视图目录,且名称和控制器的名称一样,但不包括Controller后缀。例如,HomeController对应着/Views/Home视图目录。

同时,在视图目录中,每一个控制器动作方法都有一个视图文件,文件名称和动作方法一样。动作方法通过View方法返回一个ViewResult对象。

public ActionResult Index() {

ViewBag.Message = \"欢迎光临 EntLib.com 开源电子商务平台!\";

return View(); }

控制器动作方法并没有指定视图名称。当视图名称没有指定时,动作方法返回的ViewResult将按照约定来寻找视图。首先在/Views/ControllerName目录查找和动作方法名称一样的视图,针对上面的代码,就是查找/Views/Home/Index.cshtml文件。

当然,ASP.NET MVC的约定可以覆盖。如果我们希望Index方法呈现一个不同的视图,可以传入特定的视图名称。 return View(“Default”);

这样,就会寻找/Views/Home/Default.cshtml文件。当然,在一些情况下,我们也可以指定完整的视图文件路径,使用~波浪符号提供完整视图的完整路径。

http://www.EntLib.com

25

EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程

return View(\"~/Views/Example/Index.cshtml\");

在使用波浪符号语法时,必须指定视图文件的扩展名,因为这时已经绕开了视图引擎的内部查询机制了。

添加视图

我们将修改HelloWorldController类,使用视图模板文件封装生成HTML响应返回给客户端的过程。

我们将使用ASP.NET MVC 3 引入的Razor视图引擎来创建视图模板文件。基于Razor视图模板文件的后缀为.cshtml,使用C#提供优雅(或精简)的方式创建HTML输出。Razor最小化编写视图模板所需要录入的字符,让Razor视图的开发人员觉得视图代码的编写非常流畅。

目前,Index方法返回一个字符串,且硬编码在控制器类中。我们修改Index方法返回一个View对象,代码如下所示。

//

// GET: /HelloWorld/

public ActionResult Index() {

return View(); }

这样,Index方法是用一个视图模板生成HTML响应返回给浏览器。控制器方法(也称为动作方法-Action Methods),如前面的Index方法,通常返回一个ActionResult(或集成自ActionResult的类),而不是简单的字符串类型。

在项目中,我们添加一个Index方法使用的视图模板。在Index方法中,右击并选择添加

http://www.EntLib.com

26

EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程

视图…菜单项。

在弹出的添加视图对话框中,保留默认值,点击添加按钮。

在解决方案资源管理器中,可以看到新创建的MvcMovie\\Views\\HelloWorld 文件夹和

http://www.EntLib.com

27

EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程

MvcMovie\\Views\\HelloWorld\\Index.cshtml文件。

如下是默认创建的Index.cshtml文件。

@{

ViewBag.Title = \"Index\"; }

Index

标签下面添加如下HTML脚本:

Hello from our View Template!

完整的MvcMovie\\Views\\HelloWorld\\Index.cshtml 文件如下所示:

@{

ViewBag.Title = \"Index\"; }

http://www.EntLib.com

28

EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程

Index

Hello from our View Template!

如果使用Visual Studio 2012,在解决方案资源管理器中,右击Index.cshtml文件,选择在Page Inspector中查看菜单项,

另外,也可以运行应用程序,并浏览 /HelloWorld地址。控制器中的Index方法简单运行return View()语句而已,这个表示该方法使用视图模板文件生成响应返回给浏览器。因为我们没有显式指定视图模板文件的名称,ASP.NET MVC默认使用\\Views\\HelloWorld文件夹中Index.cshtml 视图文件。下图显示了视图中硬编码的字符串Hello from our View Template。

http://www.EntLib.com

29

EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程

还不错。然而,我们发现浏览器的标题显式 – Index-我的ASP.NET MVC应用程序,并且在页面的顶部有一个很大的链接 – 将你的徽标放置在此处。

修改视图和布局页面

首先,我们想修改“将你的徽标放置在此处”的文字,这一文本在每一个页面都是一样的。虽然在应用程序的每一个页面都显示,但实际上在项目中仅在一个地方实现。访问解决方案资源管理器中的/Views/Shared 文件夹,打开_Layout.cshtml 文件。这个文件称为布局页面,在所有其他页面共享使用。

http://www.EntLib.com

30

EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程

布局模板指定了网站的HTML容器,然后在整个站点多个页面共享。代码中有

@RenderBody() 代码行,RenderBody 是一个占位符,我们创建的所有特定视图将在这里显示。例如,如果我们选择关于链接,Views\\Home\\Abount.cshtml视图将在RenderBody方法中显示。

我们修改布局模板中的站点标题,更新为“电影之家”。

@Html.ActionLink(\"电影之家\", \"Index\", \"Home\")

另外,更新标题文本:

@ViewBag.Title-电影应用程序

运行应用程序,点击关于链接,可以看到如下效果。我们在布局模板修改一次,站点中的所有页面都显示新的站点标题。

http://www.EntLib.com

31

EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程

现在,我们修改Index视图的标题。

打开MvcMovie\\Views\\HelloWorld\\Index.cshtml文件,我们修改两个地方:(1)浏览器的标题文本;(2)

元素中文本。我们简单修改这些文本,可以了解哪一部分的代码影响哪一部分的应用程序显示。

@{

ViewBag.Title = \"电影列表\"; }

我的电影列表

Hello from our View Template!

上面的代码设置了ViewBag对象的Title属性。前面我们看到了布局模板(layout template)的源代码,在HTML代码的节点使用这个值设置元素。通过使用ViewBag对象,我们可以轻松在视图模板和布局文件之间传递其他参数。<p>运行应用程序,浏览/HelloWorld,发现浏览器标题、页面的主标题和第二级标题都更新了。如果看不到更新,可能是浏览器缓存了内容,可以在浏览器中按Ctrl+F5强制刷新内容。浏览器的标题设置为ViewBag.Title的内容,另外加上在布局文件中的“-电影应用程序”文本。<p>http://www.EntLib.com<p>32<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>另外,Index.cshtml视图模板和_Layout.cshtml视图模板合并在一起,以一个单独的HTML响应发送给浏览器。布局模板可以让我们轻松修改布局,然后在整个应用程序中应用。<p>页面中Hello from our View Template文本信息是在视图中硬编码的,到目前为止,我们只用到了视图(View)和控制器(Controller),还没有使用到模型(Model)。下面我们会演示如何创建数据库,并获取模型数据。<p>从控制器传递数据给视图<p>在开始讨论数据库和模型之前,我们先了解从控制器传递数据给视图。控制器类负责响应传入的URL请求。在控制器类中,我们编写代码处理传入的浏览器请求,从数据库中检索数据,并最终决定何种响应返回给浏览器。视图模板(view template)是控制器用来创建并格式化HTML响应返回给浏览器的。<p>控制器类负责给视图模板提供数据或对象。最佳实践是:视图模板不执行任何业务逻辑或与数据库直接交互。视图模板只和控制器类提供的数据交互。保持关注点分离(separation of<p>http://www.EntLib.com<p>33<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>concerns)有助于保持代码的简洁、可测试以及可维护性。<p>目前,HelloWorlder控制器类中的Welcome动作方法接收name和numTimes参数,并直接输出给浏览器。一般不会让控制器直接返回字符串输出,我们修改控制器使用视图模板。视图模板生成动态响应,这表示需要控制器传入合适的数据给视图,供视图生成响应。我们可通过控制器在ViewBag对象中输入视图模板需要的动态数据(参数),然后视图模板可以访问这些数据。<p>返回HelloWorldController.cs 文件,修改Welcome方法,添加Message和NumTimes参数值到ViewBag对象中。ViewBag是一个动态对象,表示我们添加任何数据。ViewBag对象并没有定义属性,直到我们添加属性值。ASP.NET MVC 模型绑定系统会自动映射URL地址栏中的查询字符串命名参数(name和numTimes)给方法中定义的参数。完整的HelloWorldController.cs 代码如下所示:<p>using System;<p>using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc;<p>namespace MvcMovie.Controllers {<p>public class HelloWorldController : Controller { //<p>// GET: /HelloWorld/<p>public ActionResult Index() {<p>return View(); }<p>public ActionResult Welcome(string name, int numTimes = 1) {<p>ViewBag.Message = \"Hello \" + name; ViewBag.NumTimes = numTimes;<p>http://www.EntLib.com<p>34<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>return View(); } } }<p>现在ViewBag对象包含了将自动传递给视图的数据。接着,我们添加一个Welcome视图模板。在生成(Build)菜单中,选择生成MvcMovie菜单项,编译MvcMovie项目。 下一步在Welcome方法中,右击选择添加视图…菜单项。<p>如下是添加视图对话框:<p>http://www.EntLib.com<p>35<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>点击添加按钮,接着在新创建的Welcome.cshtml文件中的<h2>元素下面如下代码。我们创建一个循环,根据传入参数多次输出Hello 字符串。完整的Welcome.cshtml代码如下所示。<p>@{<p>ViewBag.Title = \"欢迎\"; }<p><h2>欢迎</h2> <ul><p>@for (int i=0; i < ViewBag.NumTimes; i++) { <li>@ViewBag.Message</li> } </ul><p>运行应用程序,访问如下URL地址:<p>/HelloWorld/Welcome?name=Rickie&numtimes=5<p>现在从URL中获取的数据,通过模型绑定传递给控制器。控制器将数据添加奥ViewBag<p>http://www.EntLib.com<p>36<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>对象中,并传递给对象给视图,接着视图以HTML形式显示数据给用户。<p>在上面的示例中,我们使用ViewBag对象从控制器传递数据给视图。在本教程的后面部分,我们将使用视图模型(view model)从控制器传递数据给视图。使用视图模型(view model)传递数据比ViewBag更好一些。<p>http://www.EntLib.com<p>37<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>第四章 模型(Model)<p>本章我们将添加一些类管理数据库中的电影,这些类是ASP.NET MVC 应用程序中的模型(model)部分。<p>我们将使用.NET框架的数据访问技术,如Entity Framework,来定义和处理这些模型类。Entity Framework(通常简称EF)支持Code First开发模式。Code First允许开发人员编写简单的类创建模型对象(也称为POCO类,plain-old CLR objects),接着基于类创建数据库,实现非常简洁和快速的开发流程。<p>添加模型类<p>在解决方案资源管理器中,右击Models文件夹,选择添加>类…菜单项。<p>输入类名Movie,并添加如下五个属性到Movie类中。<p>public class Movie {<p>public int ID { get; set; } public string Title { get; set; } public DateTime ReleaseDate { get; set; } public string Genre { get; set; }<p>http://www.EntLib.com<p>38<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>public decimal Price { get; set; } }<p>我们将使用Movie类表示数据库中的电影。每一个Movie对象实例表示数据库表的一行,Movie类的每一个属性将映射到数据表的一列。在相同文件中,添加如下MovieDBContext类:<p>public class MovieDBContext : DbContext {<p>public DbSet<Movie> Movies { get; set; } }<p>MovieDBContext类表示Entity Frameworkmovie数据库上下文,负责检索、存储和更新Movie对象实例到数据库中。MovieDBContext继承Entity Framework的DbContext基类。<p>为了引用DbContext和DbSet类,需要在文件顶部添加如下using 语句:<p>using System.Data.Entity;<p>完整的Movie.cs文件如下:<p>using System;<p>using System.Collections.Generic; using System.Linq; using System.Web; using System.Data.Entity;<p>namespace MvcMovie.Models {<p>public class Movie {<p>public int ID { get; set; } public string Title { get; set; } public DateTime ReleaseDate { get; set; } public string Genre { get; set; } public decimal Price { get; set; } }<p>public class MovieDBContext : DbContext {<p>http://www.EntLib.com<p>39<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>public DbSet<Movie> Movies { get; set; } } }<p>创建连接字符串和SQL Server数据库<p>创建的MovieDBContext类负责连接数据库,并映射Movie对象到数据库记录。你可能会有一个问题:如何指定连接哪一个数据库呢?我们可以在应用程序的Web.config文件中添加连接字符串信息。<p>打开应用程序根目录下的Web.config文件(注意:不是Views文件夹下的Web.config文件),添加如下连接字符串到Web.config文件的<connectionStrings>元素中。<p><connectionStrings><p><add name=\"DefaultConnection\" connectionString=\"Data Source=(LocalDb)\\v11.0;Initial Catalog=aspnet-MvcMovie-20121107205155;Integrated<p>Security=SSPI;AttachDBFilename=|DataDirectory|\\aspnet-MvcMovie-20121107205155.mdf\" providerName=\"System.Data.SqlClient\" /> <add name=\"MovieDBContext\"<p>connectionString=\"Server=.; Database=Movies; User Id=sa; Password=developer\" providerName=\"System.Data.SqlClient\" /><p>绿色背景的信息是我们需要添加到Web.config中的连接字符串,服务器采用本地SQL Server,数据库Movies,用户ID为sa,密码为developer,具体数据库连接根据实际配置进行修改。<p>接下来,我们将创建一个新的MoviesController类,使用该类显示movie数据,并允许用户创建新的movie列表。<p>http://www.EntLib.com<p>40<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>第五章 从控制器中访问模型数据<p>本章,我们将创建一个新的MoviesController类,编写代码检索movie数据,并使用视图模板在浏览器中显示数据。<p>在进行下一步之前,首先编译应用程序。<p>创建MoviesController控制器<p>右击Controller 文件夹,创建一个新的MoviesController控制器,并输入如下选项: 控制器类名: MoviesController<p>模板:包含读/写操作和视图的MVC控制器(使用Entity Framework) 模型类:Movie (MvcMovie.Models)<p>数据上下文类: MovieDBContext (MvcMovie.Models). 视图: Razor (CSHTML)<p>http://www.EntLib.com<p>41<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>点击添加按钮,Visual Studio创建如下文件和文件夹: 在项目Controllers文件夹中的MoviesController.cs文件; 在项目Views文件夹中的Movies文件夹;<p>在Views\\Movies文件夹中,创建Create.cshtml、Delete.cshtml、Details.cshtml、Edit.cshtml和Index.cshtml等等文件;<p>ASP.NET MVC 4 自动创建了CRUD(创建、读取、更新和删除)动作方法和视图,自动创建CRUD动作方法和视图的行为也称为构建基架(Scaffolding)。现在,已经创建两个一个完整功能的web应用程序,可以创建、列表、编辑和删除movie记录。<p>运行应用程序,浏览/Movies 地址。因为应用程序依赖于默认的路由,浏览器请求/Movies地址,将路由给Movies控制器的默认Index动作方法。也就是说,浏览请求/Movies相当于访问/Movies/Index。请求结果是一个空的列表,因为我们还没有添加数据记录。<p>http://www.EntLib.com<p>42<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>创建一条新的Movie记录<p>点击Create New链接,输入电影的相关数据信息,接着点击Create按钮。<p>点击Create按钮,将提交表单给服务器,在服务器端存储电影信息到数据库中。并接着转<p>http://www.EntLib.com<p>43<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>向到/Movies地址,我们可以在列表中查看到新创建的电影记录。<p>创建更多的电影记录,然后尝试Edit、Details和Delete链接操作,都工作正常。<p>检查自动生成的代码<p>打开Controllers\\MoviesController.cs文件,检查自动生成的Index方法。<p>private MovieDBContext db = new MovieDBContext();<p>//<p>// GET: /Movies/<p>public ActionResult Index() {<p>return View(db.Movies.ToList()); }<p>代码中首先实例化一个movie数据库上下文,然后使用movie数据库上下文执行查询、编辑和删除电影记录的操作。<p>请求Movies控制器返回数据库中Movies表的所有记录,并传递结果给Index视图。<p>http://www.EntLib.com<p>44<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>强类型模型和@model关键字<p>本教程前面部分,我们了解到控制器可以使用ViewBag对象传递数据给视图模板。ViewBag是一个动态对象,提供了方便的后期绑定(late-bound)的方式来传递信息给视图。 ASP.NET MVC 也提供了传递强类型的对象给视图模板,强类型的方法提供了更好的编译时代码检查,并且在Visual Studio 编辑器中提供了更好的智能提示。Visual Studio中的基架(scaffolding)机制使用这一方法来创建MoviesController 类的动作方法和视图。 打开Controllers\\MoviesController.cs 文件,检查Details方法。<p>public ActionResult Details(int id = 0) {<p>Movie movie = db.Movies.Find(id); if (movie == null) {<p>return HttpNotFound(); }<p>return View(movie); }<p>如果找到符合条件的Movie,则将Movie对象实例传递给详细视图。<p>打开Views\\Movies\\Details.cshtml文件。在创建Movie控制器时,Visual Studio 在Details.cshtml文件的顶部自动包含了如下@model语句:<p>@model MvcMovie.Models.Movie<p>通过在视图模板顶部引入@model语句,我们可以指定视图需要的对象类型。<p>@model指令允许在视图中访问控制器传入的模型对象。例如,在Details.cshtml视图模板中,将强类型Model对象的每一个字段传递给HTML辅助方法DisplayNameFor和DisplayFor。Create、Edit方法以及对应的视图模板也是传递的Movie模型对象。 打开Index.cshtml视图模板和MoviesController.cs文件中的Index方法。检查一下代码在Index动作方法中如何创建一个List对象和调用View辅助方法。接着传递Movies list<p>http://www.EntLib.com<p>45<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>对象给视图:<p>public ActionResult Index() {<p>return View(db.Movies.ToList()); }<p>当我们创建Movies控制器时,Visual Studio自动在Index.cshtml文件的顶部添加了如下@model语句:<p>@model IEnumerable<MvcMovie.Models.Movie><p>@model指令可以让我们访问控制器传入的Movie对象列表。例如,在Index.cshtml视图模板中,使用foreach代码对强类型Model对象进行遍历,获取list中的每一个子对象。<p>@foreach (var item in Model) { <tr> <td><p>@Html.DisplayFor(modelItem => item.Title) </td> <td><p>@Html.DisplayFor(modelItem => item.ReleaseDate) </td> <td><p>@Html.DisplayFor(modelItem => item.Genre) </td> <td><p>@Html.DisplayFor(modelItem => item.Price) </td> <td><p>@Html.ActionLink(\"Edit\", \"Edit\", new { id=item.ID }) | @Html.ActionLink(\"Details\", \"Details\", new { id=item.ID }) | @Html.ActionLink(\"Delete\", \"Delete\", new { id=item.ID }) </td> </tr> }<p>因为Model对象是强类型的(IEnumerable<Movie>对象),遍历中每一个item对象的类型为Movie对象。在Visual Studio 代码编辑器中,可以获得编译时(compile-time)检查和完整的智能提示。<p>http://www.EntLib.com<p>46<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>检查SQL Server数据库<p>Entity Framework Code First检测到连接字符串中Movies数据库不存在,Code First会自动创建数据库。我们可以看看SQL Server的数据库Movies,ASP.NET MVC 应用程序<p>http://www.EntLib.com<p>47<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>自动创建的数据库。<p>我们可以看看Movies表结构是如何映射到前面创建的Movie模型类的。Entity Framework Code First 根据Movie模型类自动创建相应的表结构。 可以进一步查询数据表中的记录:<p>现在,我们已经创建了数据库和一个简单列表页面显示电影记录。下面,我们将检查剩下的基架代码,并添加SearchIndex动作方法和SearchIndex视图,实现查询电影记录的功能。<p>http://www.EntLib.com<p>48<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>检查Edit 方法和Edit 视图<p>运行应用程序,浏览/Movies地址,将鼠标放置在Edit链接上,可以在左下角看到URL链接地址。<p>Edit 链接是通过Views\\Movies\\Index.cshtml视图中的Html.ActionLink方法生成的。 @Html.ActionLink(\"Edit\<p>Html 对象是一个辅助器,通过System.Web.Mvc.WebViewPage基类的属性公开出来。辅助器的ActionLink方法简化动态生成指向控制器动作方法的HTML链接。ActionLink的第一个参数是链接文本,第二个参数是调用的动作方法名称,最后一个参数是匿名对象(anonymous object),生成路由数据。<p>http://www.EntLib.com<p>49<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>上图中生成的链接地址为http://localhost:11957/Movies/Edit/2。默认的路由,在<p>App_Start\\RouteConfig.cs中定义的,使用的URL模型为{controller}/{action}/{id}。因此,ASP.NET MVC将http://localhost:11957/Movies/Edit/2请求转换为对Movies控制器中的Edit动作方法的请求,传入参数ID为2。下面是App_Start\\RouteConfig.cs默认的代码。<p>public static void RegisterRoutes(RouteCollection routes) {<p>routes.IgnoreRoute(\"{resource}.axd/{*pathInfo}\");<p>routes.MapRoute( name: \"Default\",<p>url: \"{controller}/{action}/{id}\",<p>defaults: new { controller = \"Home\", action = \"Index\", id = UrlParameter.Optional } ); }<p>我们也可以使用查询字符串传入动作方法的参数,如使用<p>http://localhost:11957/Movies/Edit?id=2 可以传入参数id=2给Movies控制器的Edit动作方法。<p>打开Movies控制器类,发现有两个Edit动作方法。<p>//<p>// GET: /Movies/Edit/5<p>public ActionResult Edit(int id = 0) {<p>http://www.EntLib.com<p>50<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>Movie movie = db.Movies.Find(id); if (movie == null) {<p>return HttpNotFound(); }<p>return View(movie); }<p>//<p>// POST: /Movies/Edit/5<p>[HttpPost]<p>public ActionResult Edit(Movie movie) {<p>if (ModelState.IsValid) {<p>db.Entry(movie).State = EntityState.Modified; db.SaveChanges();<p>return RedirectToAction(\"Index\"); }<p>return View(movie); }<p>我们注意到第二个Edit动作方法之前添加了HttpPost属性(attribute),这个属性表示重载的Edit方法仅限于POST请求调用。我们也可以再第一个Edit方法之前添加HttpGet属性,但是这个不是必须的,因为这个默认设置。<p>HttpGet Edit方法接收movie ID参数,使用Entity Framework的Find方法查询movie记录,并返回选择的movie记录给Edit视图。如果调用Edit方法时没有提供参数,ID参数设置了默认值0。如果movie记录没有找到,则返回HttpNotFound。当使用基架(scaffolding)系统创建Edit视图时,它会检查Movie类,并类的每一个属性创建代码呈现<label>和<input>元素。下面是自动生成的Edit视图的代码:<p>@model MvcMovie.Models.Movie @{<p>ViewBag.Title = \"Edit\"; }<p>http://www.EntLib.com<p>51<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p><h2>Edit</h2><p>@using (Html.BeginForm()) { @Html.ValidationSummary(true)<p><fieldset><p><legend>Movie</legend><p>@Html.HiddenFor(model => model.ID)<p><div class=\"editor-label\"><p>@Html.LabelFor(model => model.Title) </div><p><div class=\"editor-field\"><p>@Html.EditorFor(model => model.Title)<p>@Html.ValidationMessageFor(model => model.Title) </div><p><div class=\"editor-label\"><p>@Html.LabelFor(model => model.ReleaseDate) </div><p><div class=\"editor-field\"><p>@Html.EditorFor(model => model.ReleaseDate)<p>@Html.ValidationMessageFor(model => model.ReleaseDate) </div><p><div class=\"editor-label\"><p>@Html.LabelFor(model => model.Genre) </div><p><div class=\"editor-field\"><p>@Html.EditorFor(model => model.Genre)<p>@Html.ValidationMessageFor(model => model.Genre) </div><p><div class=\"editor-label\"><p>@Html.LabelFor(model => model.Price) </div><p><div class=\"editor-field\"><p>@Html.EditorFor(model => model.Price)<p>@Html.ValidationMessageFor(model => model.Price) </div><p><input type=\"submit\" value=\"Save\" /><p>http://www.EntLib.com<p>52<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p></p> </fieldset> } <div><p>@Html.ActionLink(\"Back to List\", \"Index\") </div><p>@section Scripts {<p>@Scripts.Render(\"~/bundles/jqueryval\") }<p>我们注意到视图模板顶部有@model MvcMovie.Models.Movie语句,这个表示本视图希望的数据模型类型为Movie。<p>基架代码使用了一些辅助方法输出HTML标识符。Html.LabelFor辅助方法显示字段的名分,如Title、ReleaseDate、Genre或Price等等。Html.EditorFor辅助方法显示HTML <input>元素。Html.ValidationMessageFor辅助方法显示属性验证相关的信息。 运行应用程序,浏览/Movies,点击Edit链接。然后在浏览器页面,查询页面的源代码,该页面的HTML代码如下所示。<p><form action=\"/Movies/Edit/2\" method=\"post\"> <fieldset> <legend>Movie</legend><p><input data-val=\"true\" data-val-number=\"字段 ID 必须是一个数字。\" data-val-required=\"ID 字段是必需的。\" id=\"ID\" name=\"ID\" type=\"hidden\" value=\"2\" /><p><div class=\"editor-label\"><p><label for=\"Title\">Title</label> </div><p><div class=\"editor-field\"><p><input class=\"text-box single-line\" id=\"Title\" name=\"Title\" type=\"text\" value=\"最佳女婿\" /> <span class=\"field-validation-valid\" data-valmsg-for=\"Title\" data-valmsg-replace=\"true\"></span> </div><p><div class=\"editor-label\"><p><label for=\"ReleaseDate\">ReleaseDate</label> </div><p><div class=\"editor-field\"><p><input class=\"text-box single-line\" data-val=\"true\" data-val-date=\"字段 ReleaseDate 必须是日期。<p>http://www.EntLib.com<p>53<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>\" data-val-required=\"ReleaseDate 字段是必需的。\" id=\"ReleaseDate\" name=\"ReleaseDate\" type=\"datetime\" value=\"2000/1/1 0:00:00\" /><p><span class=\"field-validation-valid\" data-valmsg-for=\"ReleaseDate\" data-valmsg-replace=\"true\"></span> </div><p><div class=\"editor-label\"><p><label for=\"Genre\">Genre</label> </div><p><div class=\"editor-field\"><p><input class=\"text-box single-line\" id=\"Genre\" name=\"Genre\" type=\"text\" value=\"喜剧\" /> <span class=\"field-validation-valid\" data-valmsg-for=\"Genre\" data-valmsg-replace=\"true\"></span> </div><p><div class=\"editor-label\"><p><label for=\"Price\">Price</label> </div><p><div class=\"editor-field\"><p><input class=\"text-box single-line\" data-val=\"true\" data-val-number=\"字段 Price 必须是一个数字。\" data-val-required=\"Price 字段是必需的。\" id=\"Price\" name=\"Price\" type=\"text\" value=\"28.00\" /> <span class=\"field-validation-valid\" data-valmsg-for=\"Price\" data-valmsg-replace=\"true\"></span> </div><p><input type=\"submit\" value=\"Save\" /> </p> </fieldset> </form><p><input>元素在HTML <form>元素内,action属性设置为提交到/Movies/Edit地址。当用户点击Edit按钮时,将表单数据提交到Server端。<p>处理POST请求<p>下面列出了Edit动作方法的HttpPost版本。<p>[HttpPost]<p>public ActionResult Edit(Movie movie) {<p>http://www.EntLib.com<p>54<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>if (ModelState.IsValid) {<p>db.Entry(movie).State = EntityState.Modified; db.SaveChanges();<p>return RedirectToAction(\"Index\"); }<p>return View(movie); }<p>ASP.NET MVC模型绑定器(model binder)接收提交过了的表单参数值,创建Movie对象,并传递给movie参数。Model.IsValid方法验证提交的表单数据可以修改(编辑或更新)Movie对象。如果数据是有效的,movie数据将保存在db(MovieDBContext实例)的Movies集合中,然后调用MovieDBContext对象的SaveChanges方法保存数据到数据库中。保存数据之后,然后重定向到MoviesController类的Index动作方法,显示movie集合,包括刚刚更新的数据。<p>如果提交的数据是无效的,则重新显示表单。Edit.cshtml视图模板中的Html.ValidationMessageFor辅助方法负责显示合适的错误消息。<p>http://www.EntLib.com<p>55<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>添加Search方法和Search视图<p>我们将添加一个SearchIndex动作方法,用来根据类型(genre)或名称(name)来搜索电影,通过URL路径/Movies/SearchIndex来访问。搜索请求页面将显示一个HTML表单,包含一个input元素,用户可以输入搜索条件。当用户提交表单时,控制器的动作方法获取查询条件,搜索数据库记录。<p>显示SearchIndex表单<p>在现有的MoviesController类中添加SearchIndex动作方法,该方法将返回一个视图,包含了一个HTML表单,代码如下:<p>public ActionResult SearchIndex(string searchString) {<p>var movies = from m in db.Movies select m;<p>if (!String.IsNullOrEmpty(searchString)) {<p>movies = movies.Where(s => s.Title.Contains(searchString)); }<p>return View(movies); }<p>第一行代码创建了LINQ查询对象,但还没有真正执行数据库查询操作。<p>如果searchString参数包含有一个字符串,则根据该参数过滤movie对象的title属性是否包含该searchString字符串。<p>s=>s.Title 代码是Lambda表达式。当在定义LINQ查询,或者调用Where或OrderBy<p>http://www.EntLib.com<p>56<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>方法时,LINQ查询并没有立即执行。这个是延后执行,表示只有到用的时候才会去执行查询,如遍历操作或者调用ToList方法。在SearchIndex范例中,查询操作在SearchIndex视图中执行。<p>现在我们实现SearchIndex视图,给用户显示表单。在SearchIndex方法中,右击选择添加视图菜单项。在添加视图对话框中,指定Movie对象为模型类,传入视图模板。在支架模板(Scaffold template)列表中,选择List,然后点击添加按钮。<p>当点击添加按钮之后,会自动创建Views\\Movies\\SearchIndex.cshtml视图模板。因为我们在支架模板(Scaffold template)列表中选择了List,因此Visual Studio在视图中自动创建了一些默认的标示符,为Movie类的每一个属性创建代码,呈现<label>元素。 运行应用程序,浏览/Movies/SearchIndex,附加查询字符串,如?searchString=画皮,过滤的电影记录如下所示。<p>http://localhost:11957/Movies/searchindex?searchString=画皮<p>http://www.EntLib.com<p>57<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>如果我们修改SearchIndex方法的原型,将参数命名为id,id参数将匹配Global.asax文件中设置的默认路由的{id}占位符。 {controller}/{action}/{id}<p>修改之前的SearchIndex方法如下所示:<p>public ActionResult SearchIndex(string searchString) {<p>var movies = from m in db.Movies select m;<p>if (!String.IsNullOrEmpty(searchString)) {<p>movies = movies.Where(s => s.Title.Contains(searchString)); }<p>return View(movies); }<p>修改之后的SearchIndex方法如下所示:<p>public ActionResult SearchIndex(string id) {<p>string searchString = id;<p>var movies = from m in db.Movies select m;<p>if (!String.IsNullOrEmpty(searchString))<p>http://www.EntLib.com<p>58<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>{<p>movies = movies.Where(s => s.Title.Contains(searchString)); }<p>return View(movies); }<p>现在,我们可以将需要传递的搜索标题作为路由数据(也就是URL节点),而不是之前查询字符串的方式。<p>http://localhost:11957/Movies/searchindex/笔仙<p>然而,我们肯定不希望用户想查询某一部电影时,每次都要修改URL地址。因此,我们需要修改UI界面,帮助过滤电影记录。前面,我们修改了SearchIndex方法,测试如何传递路由绑定的ID参数。现在我们重新修改回来,仍然让SearchIndex方法接收名称为searchString的字符串参数。<p>public ActionResult SearchIndex(string searchString) {<p>var movies = from m in db.Movies select m;<p>if (!String.IsNullOrEmpty(searchString)) {<p>movies = movies.Where(s => s.Title.Contains(searchString)); }<p>http://www.EntLib.com<p>59<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>return View(movies); }<p>打开Views\\Movies\\SearchIndex.cshtml文件,在@Html.ActionLink(\"Create New\\"Create\") 之后添加如下代码:<p>@using (Html.BeginForm()){<p>影片名称: @Html.TextBox(\"SearchString\")<br /> <input type=\"submit\" value=\"查 询\" /></p> }<p>如下是 Views\\Movies\\SearchIndex.cshtml文件添加表单之后的部分代码:<p>@model IEnumerable<MvcMovie.Models.Movie> @{<p>ViewBag.Title = \"SearchIndex\"; }<p><h2>SearchIndex</h2><p>@Html.ActionLink(\"Create New\", \"Create\") @using (Html.BeginForm()){<p>影片名称: @Html.TextBox(\"SearchString\")<br /> <input type=\"submit\" value=\"查 询\" /></p> } </p><p>Html.BeginForm辅助方法创建<form>标签的前部分,这样在用户点击查询按钮时,将表单提交给自己。<p>运行应用程序,尝试搜索影片名称。<p>http://www.EntLib.com<p>60<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>现在没有SearchIndex方法的HttpPost重载,不必重载,这是因为这个方法没有更改任何数据,仅仅过滤数据记录。<p>我们添加一个HttpPost版本的SearchIndex方法,在这种情况下,提交按钮将调用HttpPost SearchIndex方法。<p>[HttpPost]<p>public string SearchIndex(FormCollection fc, string searchString) {<p>return \"<h3> From [HttpPost]SearchIndex: \" + searchString + \"</h3>\"; }<p>运行应用程序,执行效果如下所示:<p>然而,即使添加了HttpPost版本的SearchIndex方法,但在实现上仍然存在限制。想象一下,我们希望将特定的搜索添加书签或者希望给朋友发送一个URL链接,他们也可以看到<p>http://www.EntLib.com<p>61<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>相同的搜索结果。但是,我们注意到HTTP POST请求的URL和GET请求URL是相同的(/Movies/SearchIndex),在URL地址中并没有搜索信息。搜索信息是通过表单字段值传递给Server的,这表示你无法在标签或者URL地址栏中保留搜索信息。<p>解决办法是使用一个重载的BeginForm方法,指定POST请求在URL中添加搜索信息,并路由到HttpGet版本的SearchIndex方法。替换现有的无参数传入的BeginForm方法,新代码如下所示:<p>@using (Html.BeginForm(\"SearchIndex\",\"Movies\",FormMethod.Get))<p>现在当用户提交搜索按钮时,URL将包含有查询字符串信息。搜索操作也将由HttpGet版本的SearchIndex动作方法执行,即使有了HttpPost版本的SearchIndex方法,也不会调用。<p>http://www.EntLib.com<p>62<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>添加按类型(Genre)搜索<p>如果前面添加了HttpPost版本的SearchIndex方法,现在可以删除了。<p>接着,我们将添加一个新功能,可以按类型来搜索电影。更新SearchIndex方法的代码如下:<p>public ActionResult SearchIndex(string movieGenre, string searchString) {<p>var GenreLst = new List<string>(); var GenreQry = from d in db.Movies orderby d.Genre select d.Genre;<p>GenreLst.AddRange(GenreQry.Distinct());<p>ViewBag.movieGenre = new SelectList(GenreLst);<p>var movies = from m in db.Movies select m;<p>if (!String.IsNullOrEmpty(searchString)) {<p>movies = movies.Where(s => s.Title.Contains(searchString)); }<p>if (string.IsNullOrEmpty(movieGenre)) return View(movies); else {<p>return View(movies.Where(x => x.Genre == movieGenre)); } }<p>更新后的SearchIndex方法接收一个新的参数-movieGenre,代码的前面几行创建了一个List对象,存放从数据库中获取的电影类型(movie genres)数据。 下面的代码时一个LINQ查询,从数据库中检索所有的类型信息。<p>var GenreQry = from d in db.Movies orderby d.Genre select d.Genre;<p>代码中使用了泛型List集合的AddRange方法,将所有的唯一类型添加到List中。如果不<p>http://www.EntLib.com<p>63<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>使用Distinct方法,会将重复的类型也添加进去。接着,将类型List存放在ViewBag对象的动态属性movieGenre中。<p>下面的代码演示如何检查movieGenre参数。如果不为空,代码进一步过滤电影符合指定的类型(genre)。<p>if (string.IsNullOrEmpty(movieGenre)) return View(movies); else {<p>return View(movies.Where(x => x.Genre == movieGenre)); }<p>SearchIndex视图支持按类型查询<p>在Views\\Movies\\SearchIndex.cshtml文件中添加Html.DropDownList辅助方法,就在TextBox辅助方法之前,完成后的代码如下所示:<p>@Html.ActionLink(\"Create New\", \"Create\")<p>@using (Html.BeginForm(\"SearchIndex\",\"Movies\",FormMethod.Get)){<p>影片类型: @Html.DropDownList(\"movieGenre\", \"All\") 影片名称: @Html.TextBox(\"SearchString\") <input type=\"submit\" value=\"查 询\" /></p> } </p><p>运行应用程序,浏览/Movies/SearchIndex。现在可以尝试按类型、影片名称进行搜索了。<p>http://www.EntLib.com<p>64<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>现在,我们已经检查了框架自动生成的CRUD动作方法和视图,也学会了创建搜索动作方法和视图,让用户按影片的类型和名称进行搜索。在下一章,我们将在Movie模型类中添加一个新的属性,了解如何添加一个初始化器,自动创建测试数据库。<p>http://www.EntLib.com<p>65<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>第六章 在Movie模型和表中添加一个新的字段<p>本章我们将使用Entity Framework Code First 迁移实现模型类上更新的迁移,将更新应用到数据库中。<p>默认情况下,当使用Entity Framework Code First时,会自动创建数据库,正如前面教程中我们体验过。Code First自动在数据库中创建数据表有助于跟踪数据库的Schema是否和对应的模型类同步。如果不同步,Entity Framework 会抛出错误。这样在开发解决就可以容易非常问题。<p>为模型更新设置Code First 迁移<p>在Visual Studio 2012中,选择视图 > 服务器资源管理器菜单,如下图所示。<p>在打开的服务器资源管理器面板窗口中,展开数据连接节点,右击MovieDBContext节点,选择删除菜单项。<p>http://www.EntLib.com<p>66<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>然后返回到SQL Server数据库服务器界面,可以删除之前运行应用程序过程中,自动创建的Movies数据库。<p>现在回到Visual Studio 2012,编译项目,确信没有任何错误。<p>然后选择Visual Studio 2012中的工具 > 库程序包管理器 > 程序包管理器控制台 – 菜单项,打开程序包管理器控制台面板窗口。<p>http://www.EntLib.com<p>67<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>在程序包管理器控制台(Package Manager Console)窗口,在PM>命令符后面,输入Enable-Migrations -ContextTypeName MvcMovie.Models.MovieDBContext,并按回车键。<p>上图Enable-Migrations命令在项目中创建一个新的Migrations文件夹,以及Configuration.cs文件。<p>http://www.EntLib.com<p>68<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>打开Configuration.cs 文件,更新其中的Seed方法。<p>protected override void Seed(MvcMovie.Models.MovieDBContext context) {<p>context.Movies.AddOrUpdate(i => i.Title, new Movie {<p>Title = \"笔仙\",<p>ReleaseDate = DateTime.Parse(\"1989-1-11\"), Genre = \"Romantic Comedy\", Price = 7.99M },<p>new Movie {<p>Title = \"最佳女婿 \",<p>ReleaseDate = DateTime.Parse(\"1984-3-13\"), Genre = \"Comedy\", Price = 8.99M },<p>new Movie {<p>Title = \"Hold住爱\",<p>ReleaseDate = DateTime.Parse(\"1986-2-23\"), Genre = \"Comedy\", Price = 9.99M<p>http://www.EntLib.com<p>69<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>},<p>new Movie {<p>Title = \"我的婆婆黄飞鸿\",<p>ReleaseDate = DateTime.Parse(\"1959-4-15\"), Genre = \"Comedy\", Price = 3.99M } );<p>}<p>右击带有红色波浪线的Movie类名,选择解析 > using MvcMovie.Models 菜单项。<p>上述操作,Visual Studio将自动在文件顶部添加如下using语句: using MvcMovie.Models;<p>接下来我们创建初始化迁移的DbMigration类,这个迁移将创建一个新的数据库,这就是<p>http://www.EntLib.com<p>70<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>前面删除Movies数据库的原因。<p>在程序包管理器控制台窗口,输入add-migration Initial 命令,创建初始迁移。名称Initial是随意的,只是用来命名创建的迁移文件。<p>Code First迁移在 Migrations文件夹创建了另一个雷,名称为{时间戳}_Initial.cs,这个类包含了创建数据库Schema的代码。迁移文件名称添加时间戳的前缀有助于排序。检查{时间戳}_Initial.cs文件,文件包含了创建Movies数据表。当使用如下指令更新数据库时,{时间戳}_Initial.cs 文件将会运行,并创建数据库Schema,接着执行Seed方法,向数据库中添加测试数据。<p>在程序包管理器控制台窗口,输入update-database命令创建数据库,并运行Seed方法。<p>如果出现表已经存在的错误,不能创建数据库,这个可能是因为在删除数据库之后,在执行update-database命令之前,你运行了应用程序,自动创建了数据库。在这种情况下,可以再次删除Movies数据库,并重新尝试update-database命令。如果仍然出现错误,可以删除Migrations文件夹,然后从头开始执行(也就是删除Movies数据库,并执行Enable-Migrations 指令)。<p>http://www.EntLib.com<p>71<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>运行应用程序,导航到/Movies,可以查看Seed数据可以正常显示。<p>在Movie模型中新增Rating属性<p>在现有的Movie模型中,新增Rating属性,打开Models\\Movie.cs文件,添加如下Rating属性:<p>public string Rating { get; set; } 更新之后,完整的Movie类如下所示:<p>public class Movie {<p>public int ID { get; set; } public string Title { get; set; }<p>public DateTime ReleaseDate { get; set; } public string Genre { get; set; } public decimal Price { get; set; } public string Rating { get; set; } }<p>现在,我们已经更新了Model类,同时还需要更新\\Views\\Movies\\Index.cshtml和\\Views\\Movies\\Create.cshtml视图模板,这样才可以在浏览器中显示Rating属性。<p>http://www.EntLib.com<p>72<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>打开\\Views\\Movies\\Index.cshtml文件,添加Rating列头,以及数据列显示@item.Rating属性值。如下是更新之后的Index.cshtml视图文件:<p>@model IEnumerable<MvcMovie.Models.Movie> @{<p>ViewBag.Title = \"Index\"; }<p><h2>Index</h2><p>@Html.ActionLink(\"Create New\", \"Create\") </p> <table> <tr> <th><p>@Html.DisplayNameFor(model => model.Title) </th> <th><p>@Html.DisplayNameFor(model => model.ReleaseDate) </th> <th><p>@Html.DisplayNameFor(model => model.Genre) </th> <th><p>@Html.DisplayNameFor(model => model.Price) </th> <th><p>@Html.DisplayNameFor(model => model.Rating) </th> <th></th> </tr><p>@foreach (var item in Model) { <tr> <td><p>@Html.DisplayFor(modelItem => item.Title) </td> <td><p>@Html.DisplayFor(modelItem => item.ReleaseDate) </td> <td><p>http://www.EntLib.com<p>73<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>@Html.DisplayFor(modelItem => item.Genre) </td> <td><p>@Html.DisplayFor(modelItem => item.Price) </td> <td><p>@Html.DisplayFor(modelItem => item.Rating) </td> <td><p>@Html.ActionLink(\"Edit\", \"Edit\", new { id=item.ID }) | @Html.ActionLink(\"Details\", \"Details\", new { id=item.ID }) | @Html.ActionLink(\"Delete\", \"Delete\", new { id=item.ID }) </td> </tr> }<p></table><p>接着打开\\Views\\Movies\\Create.cshtml文件,在接近form表单尾部添加如下标示符。这样当创建一个新的movie记录时,将显示一个Rating文本框。<p><div class=\"editor-label\"><p>@Html.LabelFor(model => model.Rating) </div><p><div class=\"editor-field\"><p>@Html.EditorFor(model => model.Rating)<p>@Html.ValidationMessageFor(model => model.Rating) </div><p>现在更新了应用程序代码,支持新的Rating属性。<p>我们再次运行应用程序,导航到/Movies,但是在运行过程中,出现如下异常信息:<p>http://www.EntLib.com<p>74<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>出现上述错误是因为应用程序中更新的Movie模型类和现有数据库中的Movie数据表结构不一致造成的,数据表中没有Rating数据列。<p>在本教程中,我们将使用Code First迁移来更新数据库结构。 接下来,更新Seed方法,为新增的数据列提供初始化值,打开<p>Migrations\\Configuration.cs文件,为每一个Movie对象添加Rating字段。<p>new Movie {<p>Title = \"笔仙\",<p>ReleaseDate = DateTime.Parse(\"1989-1-11\"), Genre = \"Romantic Comedy\", Price = 7.99M, Rating = \"G\" }<p>编译解决方案,接着打开程序包管理器控制台(Package Manager Console)窗口,输入如下命令:<p>add-migration AddRatingMig<p>http://www.EntLib.com<p>75<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>add-migration命令通知迁移框架检查当前Movie模型和当前数据库表结构的差异,创建必要的代码迁移新的模型更新数据库。AddRatingMig可以为随意的名称,用来命名创建的迁移文件,当然用一个有意义的名称有助于接下来的迁移过程。<p>当上述命令执行完成之后,Visual Studio 打开创建的类文件,该类继承DbMigration类,其中Up方法创建了新的数据列。<p>namespace MvcMovie.Migrations {<p>using System;<p>using System.Data.Entity.Migrations;<p>public partial class AddRatingMig : DbMigration {<p>public override void Up() {<p>AddColumn(\"dbo.Movies\", \"Rating\", c => c.String()); }<p>public override void Down() {<p>DropColumn(\"dbo.Movies\", \"Rating\"); } } }<p>现在编译解决方案,接着在程序包管理器控制台(Package Manager Console)窗口运行update-database命令。<p>http://www.EntLib.com<p>76<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>现在,可以去SQL Server界面,看看Movies数据库的变化,发现Movies数据表中已经增加了一个新的字段Rating。<p>再次运行应用程序,导航到/Movies,可以看到新增的Rating字段了,如下所示:<p>点击Create New链接,添加一条新的Movie记录,也增加了Rating文本框。<p>http://www.EntLib.com<p>77<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>点击Create按钮,在列表中将新增一条Movie记录。<p>我们还需要在Edit、Details和SearchIndex视图模板中添加Rating字段。 此时,我们再次在程序包管理器控制台(Package Manager Console)窗口输入update-database命令,将不会发生任何改变,因此此时数据库结构和模型类是匹配的。<p>http://www.EntLib.com<p>78<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>在本章,我们演示了如何修改模型类,并且让数据库同步更新。还了解了如何更新创建的数据库初始化演示数据。接着,我们将学校如何给模型类添加丰富的验证逻辑,以及执行一些业务逻辑。<p>http://www.EntLib.com<p>79<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>第七章 在模型类中添加验证逻辑<p>在本章中,我们将在Movie模型中添加验证逻辑,确保在使用应用程序的时候,用户创建或编辑Movie记录时会执行验证逻辑。<p>保持DRY(Don’t Repeat Yourself)<p>ASP.NET MVC一个核心设计原则是DRY(Don’t Repeat Yourself),ASP.NET MVC 鼓励开发人员一次指定功能或行为,然后在应用程序的任何地方都可以应用。这样,可以减少需要编写的代码量,且有助于代码维护。<p>ASP.NET MVC 和Entity Framework Code First提供的验证支持是一个很好的DRY原则示例。我们只需在模型类中定义特定验证规则,然后在应用程序的任何地方都可以执行验证规则。<p>下面我们了解如何在Movie应用程序中充分理由这一验证特性。<p>在Movie模型中添加验证规则<p>首先,在Movie模型中添加一些验证逻辑。<p>打开Movie.cs文件,在文件头部添加如下using语句,引用System.ComponentModel.DataAnnotations命名空间: using System.ComponentModel.DataAnnotations;<p>DataAnnotations提供了一组内置的验证属性(validation attributes),可用来定义任何<p>http://www.EntLib.com<p>80<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>类或属性。<p>现在更新Movie类,利用内置的Required、StringLength和Range验证属性。下面是Movie模型类中使用的一些验证属性:<p>public class Movie {<p>public int ID { get; set; } [Required]<p>public string Title { get; set; } [DataType(DataType.Date)]<p>public DateTime ReleaseDate { get; set; } [Required]<p>public string Genre { get; set; } [Range(1,100)]<p>public decimal Price { get; set; } [StringLength(5)]<p>public string Rating { get; set; } }<p>现在运行应用程序,将遇到前面相同的错误信息:<p>我们使用命令迁移更新数据库结构。先编译解决方案,然后在程序包管理器控制台(Package Manager Console)窗口,输入如下命令: add-migration AddDataAnnotationsMig<p>http://www.EntLib.com<p>81<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>update-database<p>当执行完上述命令后,Visual Studio将自动创建在Migrations目录创建XXX_AddDataAnnotationsMig.cs 文件,并打开该文件。在创建中创建了继承<p>DbMigration类的AddDataAnnotationsMig类。在Up方法中的代码,更新了数据库结构,其中Title和Genre字段不再允许为空,Rating字段最长为5。 有兴趣,可以打开SQL Server,验证一下新的Movies表结构。<p>验证属性指定了模型属性需要符合的要求。Required表示模型的属性必须有值。在这个示例代码中,有效的Movie对象中Title、ReleaseDate、Genre和Price属性必须有值。Range表示属性值必须要在指定范围内。StringLength表示字符串的最大长度,最小长度是可选的。固有内向,如decimal、int、float和Datetime,默认是必须有值的,不必设置Required标示符。<p>Code First确保在应用程序在数据库中保存更新之前,执行模型类上定义的验证规则。例如,如下代码在调用SaveChanges方法时,会抛出异常,这是因为一些必须有值的Movie对象没有赋值,且Price属性设置0,不在设置的有效范围内。 MovieDBContext db = new MovieDBContext();<p>Movie movie = new Movie();<p>movie.Title = \"Gone with the Wind\";<p>http://www.EntLib.com<p>82<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>movie.Price = 0.0M;<p>db.Movies.Add(movie);<p>db.SaveChanges(); // <= Will throw server side validation exception<p>通过.NET Framework自动执行验证规则,让应用程序更加强壮。另外,也可以避免开发人员忘记验证数据合法性,而将非法数据写入数据库。<p>用户界面验证错误信息<p>再次运行应用程序,导航到/Movies。点击Create New链接,添加一条新的Movie记录,填写表格,故意添加一些无效值,然后点击Create按钮。<p>从上图,我们看到表单自动使用红色边框标识包含无效数据的文本框,并且在右侧显示相应的错误提示消息。这些错误验证既会在客户端(使用Javascript和jQuery)执行,也会在<p>http://www.EntLib.com<p>83<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>服务器端(如果用户禁用Javascript)执行。<p>上述操作过程中,我们体会到的真正好处是我们没有修改MoviesController类的任何一行代码,也没有修改Create.cshtml视图模板来启用验证逻辑的用户界面。之前,我们创建的控制器和视图自动识别了在Movie模型类中为属性定义的验证规则。<p>Create视图和Create动作方法中的验证代码<p>你可以想知道在我们没有更新控制器或视图中的任何代码的情况下,验证UI是如何生成的呢?下面是MovieController类中的Create方法,在创建之后就一直没有更改过。<p>//<p>// GET: /Movies/Create<p>public ActionResult Create() {<p>return View(); }<p>//<p>// POST: /Movies/Create<p>[HttpPost]<p>public ActionResult Create(Movie movie) {<p>if (ModelState.IsValid) {<p>db.Movies.Add(movie); db.SaveChanges();<p>return RedirectToAction(\"Index\"); }<p>return View(movie); }<p>第一个Create动作方法(HTTP GET)显示初始的Create表单,第二个Create动作方法<p>http://www.EntLib.com<p>84<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>([HttpPost]版本)处理form提交请求。在第二个Create方法中调用ModelState.IsValid检查Movie记录是否有任何验证错误。调用这一方法将检查模型对象上的所有验证规则,如果验证错误,Create方法重新显示表单;如果没有错误,则将新的Movie记录保存到数据库中。在前面的测试过程中,在客户端检测到form表单有验证错误,就不会提交到服务器端,第二个Create方法就不会调用。如果在客户浏览器端禁用Javascript,客户端验证就会失效,HTTP POST版的Create方法会调用ModelState.IsValid检查Movie对象是否有任何验证错误。<p>下面是之前教程中使用基架模板创建的Create.cshtml视图模板,控制器中的动作方法使用该视图来显示初始的表单和验证出现错误时重新显示该视图。<p>@model MvcMovie.Models.Movie @{<p>ViewBag.Title = \"Create\"; }<p><h2>Create</h2><p>@using (Html.BeginForm()) { @Html.ValidationSummary(true)<p><fieldset><p><legend>Movie</legend><p><div class=\"editor-label\"><p>@Html.LabelFor(model => model.Title) </div><p><div class=\"editor-field\"><p>@Html.EditorFor(model => model.Title)<p>@Html.ValidationMessageFor(model => model.Title) </div><p><div class=\"editor-label\"><p>@Html.LabelFor(model => model.ReleaseDate) </div><p><div class=\"editor-field\"><p>http://www.EntLib.com<p>85<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>@Html.EditorFor(model => model.ReleaseDate)<p>@Html.ValidationMessageFor(model => model.ReleaseDate) </div><p><div class=\"editor-label\"><p>@Html.LabelFor(model => model.Genre) </div><p><div class=\"editor-field\"><p>@Html.EditorFor(model => model.Genre)<p>@Html.ValidationMessageFor(model => model.Genre) </div><p><div class=\"editor-label\"><p>@Html.LabelFor(model => model.Price) </div><p><div class=\"editor-field\"><p>@Html.EditorFor(model => model.Price)<p>@Html.ValidationMessageFor(model => model.Price) </div><p><div class=\"editor-label\"><p>@Html.LabelFor(model => model.Rating) </div><p><div class=\"editor-field\"><p>@Html.EditorFor(model => model.Rating)<p>@Html.ValidationMessageFor(model => model.Rating) </div><p><input type=\"submit\" value=\"Create\" /> </p> </fieldset> } <div><p>@Html.ActionLink(\"Back to List\", \"Index\") </div><p>@section Scripts {<p>@Scripts.Render(\"~/bundles/jqueryval\") }<p>代码中使用Html.EditorFor辅助方法为Movie对象每一个属性输出<input>元素,然后调用Html.ValidationMessageFor辅助方法。这两个辅助方法都需要访问控制器传给视图的<p>http://www.EntLib.com<p>86<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>模型对象(在这里是Movie对象),并自动查询模型属性中设置的验证属性,显示响应的错误信息。<p>这一方法的好处是控制器或Create视图模板都不需要知道实际的验证规则执行过程,或者显示具体的错误信息。所有的验证规则和错误提示信息都在Movie模型类中定义的。相同的验证规则可以自动应用到Edit视图和其他使用该Model对象的视图模板。 如果以后需要修改验证逻辑,只需要在Model模型类上修改验证属性(validation attributes)就可以了,不必担心应用程序的各个地方存在不一致的验证规则 – 所有验证逻辑在一个地方定义,可在任何地方使用。这样,可以保持代码简洁,易于维护和发展。这意味着我们需要遵守DRY准则。<p>为Movie模型添加格式化属性<p>System.ComponentModel.DataAnnotations命名空间处理提供验证属性外,还提供了格式化属性(formatting attributes)。前面,我们已经在ReleaseDate属性字段上应用了DataType枚举值。<p>[DataType(DataType.Date)]<p>public DateTime ReleaseDate { get; set; }<p>DataType属性不是验证规则,它用来告诉视图引擎如何呈现HTML内容。在上面的代码中,DataType.Date属性将显示ReleaseDate为日期格式,没有时间。例如,下面的DataType属性也不验证数据的格式: [DataType(DataType.EmailAddress)] [DataType(DataType.PhoneNumber)] [DataType(DataType.Url)]<p>仅仅是告诉视图引擎格式化数据,如为URL提供<a>标识符,为Email提供<a<p>http://www.EntLib.com<p>87<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>href=”mailto:EmailAddress.com”>。<p>除了使用DataType属性格式化数据之外,还可以显式设置DataFormatString。如下的代码设置显式ReleaseDate的日期格式,不显示具体的时间部分。 [DisplayFormat(DataFormatString = \"{0:d}\")] public DateTime ReleaseDate { get; set; } 接下来,我们将对自动生成的Details和Delete方法做一些改进。<p>http://www.EntLib.com<p>88<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>第八章 检查Details和Delete方法<p>下面我们将检查自动生成的Details和Delete方法。<p>检查Details和Delete方法<p>打开MoviesController类,检查Details方法。<p>//<p>// GET: /Movies/Details/5<p>public ActionResult Details(int id = 0) {<p>Movie movie = db.Movies.Find(id); if (movie == null) {<p>return HttpNotFound(); }<p>return View(movie); }<p>代码中使用Find方法查询数据,该方法内置了一个重要的安全特性,首先验证Find方法找到了对应的Movie记录,然后才会去处理后续工作。例如,一个黑客可通过修改URL地址,从http://localhost:xxxx/Movies/Details/1 更改为<p>http://localhost:xxxx/Movies/Details/1000,或者其他根本不存在的Movie记录。如果不检查是否为null记录,null记录将有可能导致数据库错误。 下面检查Delete和DeleteConfirmed方法:<p>//<p>// GET: /Movies/Delete/5<p>public ActionResult Delete(int id = 0) {<p>Movie movie = db.Movies.Find(id); if (movie == null) {<p>http://www.EntLib.com<p>89<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>return HttpNotFound(); }<p>return View(movie); }<p>//<p>// POST: /Movies/Delete/5<p>[HttpPost, ActionName(\"Delete\")]<p>public ActionResult DeleteConfirmed(int id) {<p>Movie movie = db.Movies.Find(id); db.Movies.Remove(movie); db.SaveChanges();<p>return RedirectToAction(\"Index\"); }<p>HTTP Get版本的Delete方法并米有删除指定的Movie记录,而是返回一个视图,用户可以提交(HTTP POST)删除操作。<p>删除记录的HttpPost方法名称为DeleteConfirmed,和HTTP GET有不同的签名或名称。 Common Language Runtime(CLR)要求重载的方法有唯一的签名(相同的方法名称,不同的参数列表)。然而,这里两个Delete方法,一个为GET,另一个为POST,都有相同的签名,它们都接收一个整型参数。<p>为了解决这一问题,我们可以给方法不同的名称,这个就是前面支架机制(scaffolding mechanism)所采用的。但这样会导致另外一个小问题:ASP.NET 映射URL地址段为动作方法的名称。如果我们修改了方法名称,正常情况下路由就无法找到这个方法了。解决这个问题的途径可以在前面的示例代码中看到,就是在DeleteConfirmed方法前面添加ActionName(“Delete”)属性声明。这样可以实现路由系统的映射,对/Delete/ URL 地址的请求将会发现DeleteConfirmed方法。<p>避免这一问题的另外一种方法是-保持相同的方法名称,但是认为修改POST方法的签名,加入一个不使用的参数。例如,一些开发人员添加一个FormCollection类型的参数,传递<p>http://www.EntLib.com<p>90<p>EntLib.com 团队编写 一步一步学习ASP.NET MVC 编程<p>给POST方法,在POST方法中不使用这一参数。<p>public ActionResult Delete(FormCollection fcNotUsed, int id = 0) {<p>Movie movie = db.Movies.Find(id); if (movie == null) {<p>return HttpNotFound(); }<p>db.Movies.Remove(movie); db.SaveChanges();<p>return RedirectToAction(\"Index\"); }<p>总 结<p>现在,我们完成了一个完整的ASP.NET MVC 应用程序,将Movie记录存放到SQL Server数据库中,提供了创建、查看、更新、删除和搜索等等功能。<p>http://www.EntLib.com<p>91<p> <div class="preview-ft"> <div class="preview-title"> <p style="color: red;"><strong>因篇幅问题不能全部显示,请点此查看更多更全内容</strong></p> <div class="model-fold-cover-bd"><a href="https://www.tang5.com/mluekx/nreivsvavoi/" target="_blank"><span>查看全文</span><i class="iconfont icon-chakangengduo"></i></a></div> </div> </div> <script type="text/javascript" src="https://jss.howto1234.com/pc/wenzhang/detail_left.js"></script> </div> <div class="content_fy"> </div> </div> <script type="text/javascript" src="https://jss.howto1234.com/pc/wenzhang/detail_foot.js"></script> <script type="text/javascript" src="https://jss.howto1234.com/pc/share_right_gg1.js"></script> <script type="text/javascript" src="https://jss.huatuo6.com/pc/share_right_xgzx.js"></script> <script type="text/javascript" src="https://jss.howto1234.com/pc/share_right_gg2.js"></script> <script type="text/javascript" src="https://jss.huatuo6.com/pc/share_right_rmyd.js"></script> </div> <div class="n_right"> <script type="text/javascript" src="https://jss.howto1234.com/pc/share_cebian_gg1.js"></script> <script type="text/javascript" src="https://jss.huatuo6.com/pc/share_cebian_rmht.js"></script> <script type="text/javascript" src="https://jss.howto1234.com/pc/share_cebian_gg2.js"></script> <script type="text/javascript" src="https://jss.huatuo6.com/pc/share_cebian_rmtw.js"></script> <script type="text/javascript" src="https://jss.howto1234.com/pc/share_cebian_gg3.js"></script> <script type="text/javascript" src="https://jss.huatuo6.com/pc/share_cebian_wntj.js"></script> </div> </div> <div class="footer"> <div class="m_box"> <div class="footer_co"> <p>Copyright © 2019-<span class="currentYear"></span> howto1234.com 版权所有 <br> <a href="https://beian.miit.gov.cn/" target="_blank">湘ICP备2023017662号-2</a></p> </div> </div> <a href="#0" class="cd-top">Top</a> <script type="text/javascript" src="https://jss.howto1234.com/pc/foot_foot.js"></script> <script type="text/javascript"> jQuery(".focusBox").hover(function () { jQuery(this).find(".prev,.next").stop(true, true).fadeTo("show", 0.2) }, function () { jQuery(this).find(".prev,.next").fadeOut() }); jQuery(".focusBox").slide({mainCell: ".pic", effect: "fold", autoPlay: true, delayTime: 600, trigger: "click"}); </script> <script type="text/javascript"> $(function () { var _line = parseInt($(window).height() / 3); $(window).scroll(function () { if ($(window).scrollTop() > 75) { $('.i_sidemenu').css({'position': 'fixed', 'top': '75px'}) } else { $('.i_sidemenu').css({'position': '', 'top': ''}) } ; }); }); $(function () { var _line = parseInt($(window).height() / 3); $(window).scroll(function () { if ($(window).scrollTop() > 1000) { $('.right_a').css({'position': 'fixed', 'top': '75px'}) } else { $('.right_a').css({'position': '', 'top': ''}) } ; }); }); </script> <script type="text/javascript"> const currentYear = new Date().getFullYear(); $('.currentYear').html(currentYear) </script> <script type="text/javascript" src="https://jss.huatuo6.com/pc/tj_foot.js"></script> </body> </html>