第12章 ASP.NET MVC
ASP.NET支持3种开发模式:WebForms、MVC和WebPages,前面各章使用的就是3种模式中最基本的Web Forms模式。Web Pages是最简单的网页开发编程模型,它提供了一种简单的方法将HTML、CSS、JavaScript及服务器代码结合起来。本章要介绍的MVC则是Microsoft最新推出的,将业务逻辑、数据及显示界面分离的一种编程模型,这种分离可实现业务逻辑、数据和显示界面松耦合的目的,大大提高了系统的可维护性。
需要说明的是,使用MVC构建应用程序时需要精心计划,由于它的内部原理比较复杂,所以需要花费一些时间去思考。同时由于模型和视图要严格分离,这样也给调试应用程序带来了一定的困难,所以MVC并不适合部署到小型应用程序中。
12.1 ASP.NET MVC概述
ASP.NET MVC是Microsoft推出的新型ASP.NET应用程序开发模式,MVC是模型(Model)、视图(View)和控制器(Controller)3个单词的缩写,它意味着这种开发模式将一个应用程序分为模型、视图和控制器3个组成部分。Visual Studio 2015中内置的MVC版本为ASP.NET MV C5。
12.1.1 MVC的组成
ASP.NET MVC应用程序的3个组成部分分别从数据和业务逻辑、用户界面及响应系统或用户请求几个方面分担了整个应用程序的实现。MVC的这种拆分有助于设计复杂的应用程序,在3个组件之间提供松耦合更利于团队开发,也能充分发挥程序员的专长。
1.模型(Model)
模型表示企业数据和业务规则,是用于存储或处理数据的组件,其主要作用是实现业务逻辑对实体类对应数据库的操作,包括数据验证规则、数据访问和业务逻辑等。在MVC的3个部件中,模型拥有最多的处理任务。被模型返回的数据是中立的,也就是说模型与数据格式无关,这样一个模型就可以为多个视图提供数据支持,增加了代码的复用率。
2.视图(View)
视图是由HTML元素组成的应用程序界面,是用户接口组件,用于将Model中的数据展示给用户。在视图中,也就是ASP.NET MVC用户界面中,HTML和CSS及其他一些页面设计技术依旧扮演着重要的角色。
3.控制器(Controller)
控制器是处理用户交互的组件,用于接收用户的输入并调用模型和视图完成用户的需求。控制器的工作方式有以下两种。
1)从Model中读取数据,通过View展示结果。
2)从View中接收用户的输入并将其传递给Model,然后重复1)的方式。
当用户单击Web页面中的超链接和发送HTML表单时,控制器本身并不输出任何东西或做任何处理。它只是接收请求并决定调用哪个模型构件去处理请求,然后再确定用哪个视图来显示返回的数据。
4.MVC的工作原理
MVC程序中3个组件的工作原理如图12-1所示。工作过程分为以下几个环节。
1)用户通过浏览器与MVC应用程序交互,用户在浏览器中发出请求(打开页面、单击按钮等)。
2)用户的请求被相应的Controller获取并处理。控制器根据需要将请求传递给相应的Model以获取需要的数据。
3)Model根据Controller传递过来的请求访问数据库,并将数据返回给调用自己的Con- troller。
4)Controller接到从Model返回的数据后,选择预设的View页面将返回结果以预设的样式展现给发起请求的用户。
图12-1 MVC工作原理
12.1.2 Razor语法
ASP.NET MVC 5默认使用Razor语法来设计动态网页。由于Razor是通过Sys-tem.Web.Mvc下的RazorViewEngine类来实现的,所以也称为“Razor视图引擎”。在ASP.NET MVC 5项目中使用的Razor视图引擎为基于Razor语法的Web网页第3版(也称为ASP.NET Web Pages 3)。
1.Razor的特点
Razor视图引擎具有以下几个特点。
1)利用Razor可以在视图(.cshtml)文件中混合使用C#和HTML、JavaScript和jQuery等服务器端和客户端语言。
2)Razor有非常简洁的语法格式。在视图文件中只需要使用一个“@”符号就可以表示C#语句块或内联表达式,这一点与jQuery中使用一个$符号来调用JavaScript脚本十分相似。
3)Razor能自动对网页中输入的字符串进行HTML编码,可以有效地防范客户端脚本攻击。
当一个MVC项目被创建后,Visual Studio将自动在网站根目录下的web.config文件中添加下列代码。
代码中的“<addkey="webpages:Enabled"value="false"/>”表示在MVC项目中不支持使用“ASP.NET Web Pages 3”模板添加网页,若将此处的false更改为true就可以使用模板添加网页,并可以通过“在浏览器中查看”来观察网页的设计效果。但由于这种方式本质上并非MVC开发模式,故一般不予采用。
2.使用Razor
(1)使用@标记
@标记表示其后跟随的是C#代码的一个内联表达式、单行语句或一个语句块;@∗……∗@表示注释语句。
例如,在视图文件中添加下列代码,将得到如图12-2所示的运行结果。
图12-2 运行结果
有以下两点需要说明。
1)Razor使用“@(变量名或表达式)”来返回C#的变量值。如果该变量名能与其他符号分隔开,则可省略一对小括号。例如,代码中返回变量xm值时需要使用小括号,而返回变量level时就可以省略小括号。
2)对于C#语句块,可使用@{…}的形式表示。若C#语句块本身就是一个结构化的代码段,则可省略一对大括号。举例如下。
(2)使用@Html.Raw()方法
对于HTML代码中出现的@符号本身或双引号、单引号等特殊符号,可以通过@Ht-ml.Raw()方法进行转义。举例如下。
<p>@Html.Raw("@符号的使用方法")</p>
<p>@Html.Raw("文件保存位置为:"d:\myfiles\file.docx"")</p>
12.1.3 创建MVC应用程序
本节通过一个简单的实例来介绍创建MVC应用程序项目的基本步骤和MVC应用程序的基本结构。
【演练12-1】创建一个简单的MVC应用程序项目。程序由Controller提供数据,由View将数据以表格的形式显示出来。
(1)创建ASP.NET MVC项目
创建一个空白ASP.NET MVC项目的操作步骤如下。
1)在Visual Studio起始页中单击“新建项目”按钮,在弹出的如图12-3所示的对话框中选择项目类型为ASP.NET Web Application(.NET Framework),并为项目指定名称和保存位置,然后单击“确定”按钮。
图12-3 新建项目
2)在弹出的如图12-4所示的对话框中选择ASP.NET的模板为“空”,并选择为项目添加MVC核心引用,然后单击“确定”按钮。如果希望在项目中使用Web Forms或Web API的功能,可同时向项目中添加相应的核心引用。
(2)添加Controller
空白ASP.NET MVC应用程序项目创建后,在解决方案资源管理器中右击Controllers文件夹,在弹出的快捷菜单中选择“添加”→“控制器”命令。在弹出的“添加基架”对话框中选择“MVC 5控制器-空”模板并单击“添加”按钮,如图12-5所示。在弹出的对话框中将添加的控制器命名为NewController(控制器的名称应以Controller结尾),单击“添加”按钮。命令执行后,系统将在Controllers文件夹中创建一个名为NewController.cs的文件(控制器的代码应书写到该文件中)。同时,在Views文件夹中创建一个名为New(与控制器同名)的子文件夹。
图12-4 选择MVC模板
图12-5 向项目中添加一个空白控制器
在解决方案资源管理器中双击打开NewControllers.cs,按照以下所示编写Index()方法的代码(控制器中的方法称为Action或动作)。
(3)添加View
在解决方案资源管理器中右击Views文件夹下的“New”子文件夹,在弹出的快捷菜单中选择“添加”→“视图”命令,在弹出的如图12-6所示的对话框中指定视图的名称为Index(本例中未选择“使用布局页”复选框),单击“添加”按钮。命令执行后,系统将在New文件夹中创建一个名为Index.cshtml的视图页文件。.cshtml文件的格式与HTML文件夹的格式十分相似,与控制器交互的部分使用“@”标注(这一点与早期版本的“<%=…%>”不同)。
图12-6 添加视图
在解决方案资源管理器中双击打开Index.cshtml,按照以下所示编写其代码。
运行Index.cshtml文件,将在浏览器中得到如图12-7所示的结果(注意:运行其他文件时程序会出现404资源未找到的错误)。
图12-7 程序运行结果图
12.1.4 ASP.NET MVC项目的组成
创建一个ASP.NET MVC项目后,Visual Studio会自动为项目添加一系列的配置文件和用于保存各类文件的文件夹。图12-8所示为在解决方案资源管理器中看到的【演练12-1】中创建的ASP.NET MVC项目的系统结构。
1.应用程序信息文件
1)Properties文件夹:项目属性文件夹,其中包含有一个名为AssemblyInfo.cs、用于保存程序集信息(如名称、版本和版权等)的文件。该文件由项目“属性”窗口中的各选项生成,一般不需要手工编辑。
2)引用文件夹(bin):用于存放系统运行所必需的外部引用(各类.dll文件)。
2.项目文件夹
1)App_Data文件夹:用于存放项目中使用的数据文件或数据库文件,其作用及功能与Web Forms项目中的App_Data文件夹相同。
2)App_Start文件夹:用于存放配置类的代码文件,默认包含一个名为RouteConfig.cs的路由配置文件,也就是ASP.NET MVC项目的“路由表”。
图12-8 最基本的MVC项目结构
3)Controllers文件夹:用于存放项目中所有控制器文件的文件夹。
4)Models文件夹:用于存放项目中所有模型文件的文件夹。
5)Views文件夹:用于存放项目中所有视图的文件夹。每个视图以对应控制器名为子文件夹名,视图文件的扩展名为cshtml。
3.配置文件
1)ApplicationInsights.config:监视配置文件,用于设置如何监视应用程序的运行情况。
2)Global.asax:全局配置文件,用于设置全局URL路由的默认值,在应用程序启动时可通过该文件执行一些特殊操作。
3)packages.config:项目中附加的软件包配置文件,由系统自动生成和管理,一般不需要程序员编写和修改。
4)Web.config:XML格式的网站或文件夹的配置文件。其作用与Web Forms项目中的web.config文件相同。
12.2 ASP.NET MVC路由
MVC路由是指用来将用户请求与控制器对应的处理方法进行关联的设置,它也是除了模型、视图和控制器外最为重要的一个概念,分为入站路由和出站路由两种类型。在前面几章介绍过的Web Forms开发模式中,所有的用户请求都会指向一个物理存在的文件(如.aspx、.html等),而在MVC中用户请求则一律指向控制器中的某个操作方法,并由该方法决定下一步的操作(如打开视图、获取数据等)。
12.2.1 MVC路由
在ASP.NET MVC中,所有用户请求都要首先经过MVC的路由系统才能找到处理该请求的控制器中对应的Action方法。
1.注册路由
所谓“注册路由”,就是通过存放在App_Start文件夹下的RouteConfig.cs所表述的RouteConfig类中定义的RegisterRoutes()方法,将URL、控制器及对应的Action关联起来。
MVC应用程序启动后,首先会调用全局配置文件Global.asax中定义的Application_Start()方法。其中的代码如图12-9所示(以【演练12-1】为例)。
图12-9 Global.asax中的代码
Application_Start()方法中通过调用RouteConfig类的RegisterRoutes()方法,注册了一个应用于整个项目的“全局路由”。参数RouteTable.Routes是一个静态集合对象,用于存放路由数据(RouteData)。当应用程序规模较大时,可以通过区域(Areas)将其划分为较小的单元。而且每个单元都可以有自己的路由规则,代码中的“AreaRegistration.RegisterAllAreas();”语句就是用于注册基于区域的路由规则的。
图12-10所示为由系统自动创建的RouteConfig类的代码。RegisterRoutes()方法的参数routes是一个RouteCollection类型的集合对象,用于接收调用语句传递过来的路由数据。
图12-10 RouteConfig.cs中的代码
RegisterRoutes()方法中最重要的是由“routes.MapRoute()”语句表述的“路由表”的定义。它通过name、url和defaults这3个参数来规定URL路径、控制器及其Action之间的关系。
1)name:表示路由名称,Default是默认路由名称。
2)url:以占位符的形式表示的URL格式,它使用以“/”分隔的3级结构:控制器、对应的方法和传递的数据。
举例如下。
http://localhost/home/index/1 //表示指向HomeController的Index1)方法,并传递参数1
http://localhost/New/Details/2 //表示指向NewController的Details1)方法,并传递参数2
3)defaults:一个带有3个属性的匿名类型,提供了默认路由的默认数据。表示对应控制器是HomeController,对应的Action方法是Index,第3个属性id表示所传递的数据。可以理解为在程序启动时默认指向HomeController控制器的Index方法,同时向Index方法传递数据id。
理解了这些,就能看出在【演练12-1】中并不存在系统默认的HomeController控制器,只有一个手工创建的NewController控制器。所以将defaults属性中的“controller="Home"”改为“controller="New"”,就可以在任何位置正确运行程序,而不会出现404错误了。
2.从URL向控制器传递数据
在MVC程序中可以通过Controller类的RouteData属性或查询字符串Request.QueryString[]进行页面间的数据传递。
【演练12-2】使用URL向控制器传递数据示例。
程序设计步骤如下。
1)新建一个ASP.NET空MVC项目(不使用布局页和单元测试),手工添加控制器和视图,并按下列代码所示编写路由注册(RouteConfig.cs)、控制器(DefaultController)和视图(Index.cshtml)代码。
2)编写RouteConfig.cs的代码如下。
3)编写DefaultController.cs的代码如下。
4)编写Index.cshtml的代码如下。
5)按<F5>键运行程序后Index.cshtml中显示如图12-11所示的内容,从浏览器地址栏中可以看到此时的URL为“http://localhost:xxxx/”(xxxx为随机端口号)。
如果在地址栏中输入“http://localhost:xxxx/default/index/张三/12345678”后按<En- ter>键,将得到如图12-12所示的结果。
如果在地址栏中输入“http://localhost:xxxx/default/index/李四/87654321”后按<Enter>键,将得到如图12-13所示的结果。
图12-11 默认状态
图12-12 传递管理员参数
图12-13 传递普通用户参数
需要注意以下几个问题。
1)从RouteConfig.cs中可以看出,URL中包含4个占位符{controller}/{action}/{id}/{plus},且占位符的名称与实际内容无关(第3个占位符中实际存放的是用户名,第4个占位符中实际存放的是用户电话号码)。
2)使用“http://localhost:xxxx/”URL地址访问网站时,{controller}参数为DefaultCon-troller,{action}参数为Index,{id}和{plus}参数为空,自然就得到了如图12-11所示的结果。
3)使用“http://localhost:xxxx/default/index/张三/12345678”URL地址访问网站时,{id}参数为“张三”,{plus}参数为12345678,自然就得到了图12-12所示的结果。
4)MVC项目与WebForms项目相同,都可以通过查询字符串Request.QueryString[]接收URL中通过“?”传递过来的参数。例如,在浏览器地址栏中输入以下URL后按<En-ter>键,将得到如图12-14所示的结果。
http://localhost:xxxx/default/index/张三/12345678?id=0001
图12-14 接收通过“?”传递过来的参数
12.2.2 通过路由实现超链接和页面跳转
实际应用中常会用到超链接和页面跳转,最典型的处理方法就是在页面中添加<a>…</a>标记或使用Response.Redirect()方法。举例如下。
<a href="admin/main.html">进入管理页面</a> //书写在HTML页面代码中
Response.Redirect("~/admin/main.html");//书写在服务器控件的后台事件中
在ASP.NETMVC项目中一般会使用路由及相关的方法,从而更加灵活地输入超链接和实现页面间的跳转。
1.通过路由创建超链接
在ASP.NET MVC项目中如果希望请求一个特殊的路由,可以使用HtmlHelper类中的RouteLink()方法,该方法用于动态地在页面中生成一个超链接。举例如下。
书写在视图页中的上述代码,能根据变量ulevel的不同输出不同的热点文字和目标URL。当ulevel的值为“管理员”时,显示“进入管理页面”,超链接相对地址为“/Ad-min/Index/1”。否则,热点文字为“修改个人信息”,超链接相对地址为“/Guest/Index/1”。其中,Admin和Guest为控制器名,Index为控制器中的方法名,1为传递的参数。
HtmlHelper类中还提供了一个ActionLink()方法,该方法用于动态生成一个指向某控制器方法的超链接。举例如下。
@Html.ActionLink("关于我们","About","Default")
书写在视图页中的上述代码创建了一个指向“http://localhost:xxxx/About”的超链接,热点文字为“关于我们”,指向Default控制器的About方法。
2.通过路由实现页面跳转
在ASP.NET MVC项目中仍然可以使用Response.Redirect()方法实现页面跳转。而更常用的方式是利用事先定义好的路由来实现页面间的跳转。
【演练12-3】使用RouteLink()方法实现页面间的跳转,并与<a>…</a>标记生成的静态超链接进行比较。程序运行后显示如图12-15所示的first.cshtml页面,其中显示有一个使用<a>…</a>标记生成的静态超链接,热点文字为“a标记生成的静态超链接”。单击该超链接后跳转到如图12-16所示的second.cshtml页面,其中显示有一个使用RouteLink()方法生成的超链接,热点文字为“RouteLink生成的超链接”。单击该超链接后可返回first.cshtml页面。
图12-15 first.cshtml页面
图12-16 second.cshtml页面
程序设计步骤如下。
1)新建一个ASP.NET空MVC项目(不使用布局页和单元测试),通过手工的方式添加控制器和视图。
参照前面介绍的方法,在解决方案资源管理器中添加一个控制器(DefaultController.cs)和两个视图页first.cshtml、second.cshtml。
2)在解决方案资源管理器中右击项目名称,在弹出的快捷菜单中选择“属性”命令,在弹出的如图12-17所示的对话框中选择设置对象为Web,设置“启动操作”的类型为“特定页”,并填写特定页为“default/first”(Default控制器的First()方法)。
图12-17 设置从特定页启动
3)按以下所示编写RouteConfig.cs的代码。
4)按以下所示编写DefaultController.cs的代码。
5)按以下所示编写first.cshtml的代码。
6)按以下所示编写second.cshtml的代码。
12.3 ASP.NET MVC控制器
控制器是ASP.NET MVC应用程序的“指挥中心”,而视图则构成了应用程序的用户界面。在前面几节中已初步接触到了最基本的控制器和视图的使用方法,本节将对它们进行更加深入的了解。
控制器的作用是处理传入的请求、接收用户输入和展现处理结果,以及执行应用程序逻辑。所有的控制器均继承于ControllerBasc类,通过该类可以进行常规的MVC处理。
控制器继承于Controller类,Controller又继承于ControllerBase类。但由于ControllerBase类中包含的成员数量较少,而Controller却包含非常丰富的成员,所以Controller类是控制器的默认实现,它负责以下几个处理阶段的工作。
1)查找要调用的Action方法,并验证该方法是否可以被调用。
2)获取要执行的Action方法的参数。
3)处理在执行操作期间可能发生的错误。
4)提供呈现视图的默认引擎。
在一个控制器中可以定义一个或多个Action方法,一个Action方法也可以控制一个或多个视图。
12.3.1 控制器的常用属性和方法
控制器中常用的属性有ViewData、ViewBag、TempData、Server、Request和Response等。在Action方法或视图中可以通过这些属性访问相关的对象,从而实现控制器与视图之间的数据传递。
1.ViewData和ViewBag属性
ViewData属性是一个ViewDataDictionary对象,是一个不区分大小写的由“键/值”(Key/Value)对组成的字典类型集合,也就是说ViewData中的每一个元素都由“键”和“值”一对数据组成。举例如下。
ViewData["msg"]="ASP.NET MVC";
其中,msg为键,ASP.NET MVC为值,可以理解为“ViewData集合中键名为msg的元素”。
ViewBag属性是ViewData的另一种表示形式,也是一种由“键/值”(Key/Value)对组成的字典类型集合。该属性返回的是一种动态数据类型(Dynamic),这种数据类型只有在进行编译时才会被系统处理。下列代码演示了ViewData与ViewBag在语法上的不同。
ViewData["Name"]="张三";
ViewBag.Name="张三";
又如:
在ASP.NET MVC项目中通常需要在控制器中使用ViewData或ViewBag属性定义要传递给视图的数据,然后在视图中再将这些数据展示出来。
2.TempData属性
TempData属性的作用与ViewData和ViewBag相似,也用来向视图传递数据。不同的是它可以在不同的视图间进行数据传递。此外,TempData是一个临时的键值对数据集合,只能在当前请求时读取该对象,再次请求时其中的所有数据都会自动变成null。从MVC内部的实现来看,TempData实际上是通过Session来实现的,这样既可以区分不同的访问者,又能及时清除这些暂存的数据。
TempData的另一个典型用法是,在数据重定向到另一个Action方法之前先通过Temp-Data存储要传递的数据,然后再从另一个Action中得到这些数据。
3.Server属性
在控制器中,利用Controller类公开的Server属性可以获取ControllerBase类中定义的Ht-tpServerUtility对象,然后通过该对象在服务器上执行一些特定的操作。例如,对HTML字符串和URL字符串进行编码和解码、将虚拟路径转换成物理路径等。Server属性提供的常用方法如表12-1所示。
表12-1 Server属性提供的常用方法
这些方法与第5章中介绍过的Web Forms编程模式中的Server对象的相关方法十分相似,读者可参阅前面的内容以加深理解。
4.Request和Response属性
在Action方法或视图的C#代码块中,通过Request属性可以获取HttpRequestBase对象,从而进一步调用该对象提供的一些属性和方法,如Request.Cookies[key]、Request.Files[key]、Request.Form[key]和Request.QueryString等。下列书写在Action方法中的代码演示了Request属性的使用过程。
在视图文件(∗.cshtml)中通过“@ViewBag.Result”语句即可将上述HTML代码直接输出到页面中。
控制器还提供了一个只读的Response属性,其使用方法与第5章中介绍的Response对象的使用方法完全一致,这里不再赘述。
12.3.2 控制器的Action()方法
ASP.NET MVC应用程序通过路由设置找到相应的Action()方法后,下一步工作就是将请求中携带的数据传递给Action()方法的参数。
1.Action()方法参数的来源和映射
ASP.NET MVC框架可以将Action()方法中的参数值自动映射到Action()方法。在默认情况下,若Action()方法带有参数,则ASP.NET MVC会检查是否存在同名参数,若有则自动传递给Action()方法,无须再编写从请求中获取参数的代码。
Action()方法参数的主要来源如表12-2所示。
表12-2 Server属性提供的常用方法
所谓“参数映射”,是指在Action()方法中设置用于接收通过上述方法传递过来的参数值的设置。例如,下列代码通过FormCollection类型变量formValues实现了参数映射。当Create()方法被调用时,控制器自动接收调用者传递过来的参数,并将其存放在formValues中。完成参数映射后才会转去执行Action()方法中的代码。
publicActionResult Create(FormCollection formValues)
{…}
除了上述常规的参数映射方式外,ASP.NETMVC还支持Action()方法的可选参数。例如下列代码表示Show()方法使用日期作为参数,如果没有获得参数值,则将当前日期作为默认值。
2.ActionResult的返回值类型
ActionResult类是所有操作结果的基础,多数Action()方法会返回从该类派生的子类实例。例如,常见的操作是调用View()方法返回一个从ActionResult类派生的ViewResult类的实例。当然也可以根据实际需要返回任意类型(如字符串、整数或布尔值等)的对象。不管返回值是何种类型,它们在呈现到响应流之前,都会被封装在ActionResult类型的对象中。
表12-3列出了常见的ActionResult的子类型,通过它们可以实现各种内容的输出。
表12-3 ActionResult的子类型及说明
【演练12-4】ActionResult应用示例。程序运行后显示如图12-18所示的界面,当用户单击某个超链接时将显示出相应的结果,如图12-19所示。
单击3和4时,可下载并播放指定的MP3文件和在浏览器中打开PDF文件。
图12-18 程序界面
图12-19 单击1和2时显示的结果
程序设计步骤如下。
1)新建一个ASP.NET空MVC项目(不使用布局页和单元测试),通过手工方式添加控制器和视图。参照前面介绍的方法,在解决方案资源管理器中添加一个控制器DefaultCon-troller.cs和一个视图页Index.cshtml。
在项目文件夹中创建一个名为Content的子文件夹,将用于测试的MP3文件和PDF文件复制到其中。创建一个Scripts子文件夹,将需要引用的“jquery.unobtrusive-ajax.js”和“jquery-3.1.1.min.js”复制到其中。上述.js文件可以从Internet中下载,也可以通过本书第8章中已介绍过的NuGet来获取。
2)按以下所示编写DefaultController.cs的代码。
3)按以下所示编写Index.cshtml的代码。
有以下两点需要说明。
1)缺少了对“jquery-3.1.1.min.js”和“jquery.unobtrusive-ajax.js”的引用将无法使用Ajax.ActionLink()方法。
2)在设计“JavaScriptResult示例”和“JsonResult示例”时,若使用Html.ActionLink()方法创建超链接,将不能得到预期的效果(JavaScript会按文本输出脚本内容,不能弹出信息框;Json会报错)。
12.4 ASP.NET MVC视图和模型
在前面介绍过的内容中已经初步接触了一些关于视图的概念和一些简单的视图文件(.cshtml)。可以看出一个cshtml文件包含了两大类代码:客户端代码和服务器端代码。其中客户端代码包括HTML、CSS、JavaScript和jQuery等,服务器端代码由使用Razor语法编写的C#代码构成。细心的读者会注意到ASP.NET MVC放弃了前端页面与后台代码紧耦合的ASP.NET服务器控件,页面的构成完全由客户端代码来实现。
12.4.1 布局页的概念
布局页也称为母版页,是指可以被其他页面作为模板来引用的特殊网页,通常用来布局页面中固定不变的部分,与ASP.NET WebForms项目中母版页(.master)的概念相似。布局页通常被保存在网站文件夹下的Views/Shared子文件夹中,并且其文件名都以下画线“_”开头(如_Layout.cshtml、_AreasLayout.cshtml等)。对于规模较大的网站,通常使用区域(Areas)来管理项目,每个区域都可以单独拥有自己的布局页模板。
布局页中使用RenderBody()方法指定视图页或视图的显示位置,使用RenderSection()方法显示视图中定义的“节”内容。
例如,可以将网站的标题横幅区、版权页脚区、左侧导航区,以及对.css和jQuery的引用等公共内容设计到布局页中,而将主显示区的动态内容通过“@RenderBody()”方法指定其显示位置。需要注意的是,RenderBody()方法不带任何参数,且在布局页中只能出现一次。
如果在视图页中使用“@Section名称”定义了一个“节”,则可在布局页中通过调用RenderSection()方法指定该“节”显示的位置。在布局页中引用“节”的语法格式如下。
@RenderSection(secname[,required=true_required=false])
在视图页中定义“节”的语法格式如下。
12.4.2 使用布局页
【演练12-5】使用布局页控制视图的样式,程序运行后显示如图12-20所示的页面。其中,Logo(标题栏)、Nav(导航栏)、Left(左侧栏)和Bottom(版权栏)板块均由布局页配合CSS样式表创建,并通过RenderBody()和RenderSection()方法指定视图和Section的显示位置。
图12-20 使用布局页的视图
程序设计步骤如下。
新建一个ASP.NETMVC空网站项目,向项目中添加一个HomeController控制器。
(1)设计CSS样式表
在解决方案资源管理器中右击项目名称,在弹出的快捷菜单中选择“新建文件夹”命令,在网站根目录下创建一个用于存放CSS样式表文件、名为Content的文件夹。右击Con-tent文件夹,在弹出的快捷菜单中选择“添加”→“样式表”命令,按以下所示编写StyleSheet1.css的代码。
(2)设计布局页
在解决方案资源管理器中右击Views文件夹,在弹出的快捷菜单中选择“新建文件夹”命令,在Views下创建一个用于存放布局页、名为Shared的子文件夹。右击Shared文件夹,在弹出的快捷菜单中选择“添加”→“MVC5布局页(Razor)”命令,向Shared文件夹中添加一个名为“_LayoutPage1.cshtml”的布局页,并按以下所示编写代码。
(3)添加视图
在解决方案资源管理器中右击Views下的Home文件夹,在弹出的快捷菜单中选择“添加”→“视图”命令,在弹出的如图12-21所示的对话框中指定视图名称为Index,选择使用前面创建的布局页后单击“添加”按钮。按以下所示编写Index.cshtml的代码。
图12-21 添加视图
12.4.3 通过模型向视图传递数据
模型(Model)是ASP.NE TMVC项目的数据接口。数据库、XML文件、Web API及其他各种服务(Web Service)中的数据都可以通过模型传递给控制器。用户提交的数据也可以通过模型传递给数据库。
通过模型向视图传递数据一般需要经过以下3个步骤,在Models文件夹下创建模型类;在控制器中获取模型数据;最后在视图中显示模型数据。本节将通过一个简单的实例说明通过模型向视图传递数据的具体实现。
【演练12-6】在ASP.NET MVC项目的Models文件夹下创建一个用于存放学生信息的MyModel.cs模型类,该类具有StuNo、Stu-Name和StuAge共3个属性。在控制器的Ac-tion()方法中声明一个MyModel类型的泛型集合,将3名学生的信息存入集合并返回给视图。在视图中获取由模型传递过来的数据,并将其显示到表格中。程序运行结果如图12-22所示。
程序设计步骤如下。
图12-22 在视图中显示从模型获取的数据
创建一个ASP.NETMVC空项目,向项目的Controller文件夹中添加一个控制器Home-Controller.cs;向Views下的Home文件夹中添加一个不使用布局页的Index.cshtml视图。
(1)创建模型类
在解决方案资源管理器中右击Models文件夹,在弹出的快捷菜单中选择“添加”→“类”命令,向Models中添加一个名为MyModel.cs的模型类文件。按以下所示编写MyMod-el.cs的代码。
(2)获取模型数据
在代码窗口中打开HomeController.cs控制器文件,按以下所示编写其代码。
(3)显示模型数据
在代码窗口中打开Index.cshtml视图文件,按以下所示编写其代码。
说明:在视图文件中,可以使用@model(首字母小写)声明控制器传递过来的模型类型,该类型必须与模型类型一致,如本例的“@modelList<ex12_6.Models.MyModel>”。而后,可以使用@Model(首字母大写)访问模型中对应的属性,如本例的“@foreach(var vinModel)”。
12.4.4 使用ADO.NET实体数据模型
在ASP.NET MVC项目中可以使用ADO.NET实体数据模型,创建能对数据库进行常规操作的应用程序。这种方式与在ASP.NET Web Forms项目中使用数据源控件类似,几乎不需要程序员编写任何代码,在向导的指引下即可十分方便地完成程序设计。本节将以管理SQL Server数据库(TelBook)中的通讯录(Tel)表为例说明ADO.NET实体数据模型的操作方法。
【演练12-7】使用ASP.NET实体数据模型创建一个能对SQL Server数据库进行常规操作的MVC项目。
程序运行后显示如图12-23所示的Index.cshtml页面(如果初始状态下没有在数据库中输入任何记录,此处为一个空表)。
单击“添加记录”按钮将打开如图12-24所示的Create.cshtml页面,用户在填写了新记录的相关数据后,单击“添加记录”按钮,程序将自动返回到Index.cshtml页面,并将新记录显示到表格中。若要放弃添加,可直接单击“返回列表”按钮。
图12-23 查看记录Index.cshtml页面
图12-24 添加记录Create.cshtml页面
若在Index.cshtml中单击“编辑”按钮,将打开如图12-25所示的Edit.cshtml页面,其中显示有当前记录的现有数据,用户在修改后单击“保存”按钮,可将修改后的数据提交到数据库。若要放弃修改可直接单击“返回列表”按钮。
若在Index.cshtml中单击某条记录前面的“删除”链接,将打开如图12-26所示的De-lete.cshtml删除确认页面。单击“删除”按钮进行确认,若放弃删除可直接单击“返回列表”按钮。
图12-25 修改记录Edit.cshtml页面
图12-26 确认删除Delete.cshtml页面
程序设计步骤如下。
(1)创建MVC项目和数据库
创建一个ASP.NET MVC空项目,在解决方案资源管理器中右击App_Data文件夹,在弹出的快捷菜单中选择“添加”→“SQL Server数据库”命令,向项目中添加一个名为Tel-Book的数据库,并向数据库中添加一个名为Tel的数据表,该表中包含id(编号,自增值整型)、name(姓名)和telnum(电话号码)3个字段。
(2)添加ADO.NET实体数据模型
1)在解决方案资源管理器中右击Models文件夹,在弹出的快捷菜单中选择“添加”→“新建项”命令,在弹出的如图12-27所示的对话框中选择“数据”分类下的“ADO.NET实体数据模型”选项,并指定模型名称后单击“添加”按钮。
图12-27 添加ADO.NET实体数据模型
2)在弹出的如图12-28所示的“选择模型内容”对话框中选择“来自数据库的EF设计器”选项,然后单击“下一步”按钮。所谓EF,是指Entity Framework(实体框架),Visual Studio2015内置了对EF5和EF6的支持。
3)在如图12-29所示的“选择您的数据连接”对话框中,选择前面创建完毕的Tel-Book数据库后单击“下一步”按钮。
图12-28 指定模型包含的内容
图12-29 选择要连接的数据库
4)在如图12-30所示的“选择您的版本”对话框中,选择“实体框架6.x”单选按钮,然后单击“下一步”按钮。在如图12-31所示的“选择您的数据库对象和设置”对话框中,选择操作对象为前面创建的TelBook数据库中的Tel表,然后单击“完成”按钮。
图12-30 选择EF版本
图12-31 选择数据库对象
注意:ADO.NET实体数据模型添加完毕后,需要执行一次Visual Studio“生成”菜单→“重新生成解决方案”命令,否则后续的操作会出现错误。
(3)添加MVC控制器
ADO.NET实体数据模型添加完毕后,可在解决方案资源管理器中右击Controllers文件夹,在弹出的快捷菜单中选择“添加”→“控制器”命令。在如图12-32所示的“添加基架”对话框中选择“包含视图的MVC 5控制器(使用Entity Framework)”选项,然后单击“添加”按钮。
图12-32 添加包含视图的MVC5控制器
在弹出的如图12-33所示的对话框中需要选择和设置为ADO.NET实体数据模型提供支持的模型类和数据上下文类。
图12-33 设置模型类和数据上下文类
在设置上下文类时,若系统提供的选项中没有与Models文件夹中相同的,可单击选择框右侧的“+”按钮,进行手工修改。请读者自行比较一下解决方案资源管理器中由系统自动生成的上下文类的名称,进而理解这里为何要这样设置。
本例中未使用布局页,因此需要取消选择“使用布局页”复选框。控制器的名称一般情况下不需要修改,设置完毕后单击“添加”按钮。
(4)修改视图文件代码
完成了前3步的操作后,一个能对SQL Server数据库进行常规操作的MVC应用程序基本也就完成了,可按<F5>键运行程序并检测其基本功能。若希望使程序界面基本符合用户的一般使用习惯,可按以下所示修改由系统自动生成的Index.cshtml、Create.cshtml、Ed-it.cshtml和Delete.cshtml这4个视图文件,并向项目中添加必要的CSS样式文件。系统自动生成的还有一个用于显示记录细节的Details.cshtml视图文件,在本例中其作用不大,可以在Index.cshtml中删除向视图转换的超链接,将其屏蔽。
1)修改Index.cshtml代码如下。
2)修改Create.cshtml代码如下。
3)修改Edit.cshtml的代码如下。
4)修改Delete.cshtml的代码如下。
思考:观察一下App_Data文件夹中的内容。在应用程序中添加的数据库记录被保存到了哪里?为什么?
12.5 实训——设计一个用户管理程序
12.5.1 实训目的
1)通过本实训熟练掌握使用ADO.NET实体数据模型,创建用于管理SQL Server数据库的MVC应用程序的一般步骤。
2)通过修改由系统自动生成的视图页面,进一步熟悉使用CSS样式表、HTML标记语言和Razor语法编写网页代码的技巧。
3)通过阅读由系统自动生成的相关控制器文件代码和模型文件代码,初步建立通过模型管理数据库的概念。
12.5.2 实训要求
1)新建一个MVC空项目,向项目中添加一个名为Admin的SQL Server数据库,向数据库中添加一个名为Users的数据表,该表具有id(编号)、uname(用户名)、upwd(密码)和ulevel(级别)4个字段(字段类型和宽度可根据需要自行设计)。
2)使用ADO.NET实体数据模型,创建一个能对上述数据库进行常规操作(如浏览、添加记录、修改记录和删除记录)的MVC项目。
3)修改由系统自动生成的视图文件,使之基本符合用户的一般操作习惯(使用中文提示、使用表格布局等)。