文章教程

18.2ASP.NETMVC应用程序

8/31/2020 9:52:39 PM 人评论 次浏览

18.2 ASP.NET MVC应用程序

ASP.NET MVC框架应用程序把URLS映射到服务器代码,它不是把URLS映射到存储在硬盘上的.aspx文件或处理器,而是把URLS映射到控制器类。控制器类处理传入的诸如用户输入和交互请求,并执行相应的应用程序和数据逻辑,最后控制器类通常调用视图组件来生成HTML输出。

18.2.1 MVC应用程序的创建

ASP.NET MVC框架包含一个Visual Studio项目模板,这个模板可以为创建基于MVC设计模式的Web应用程序提供帮助。它创建一个新的MVC Web应用程序,并且提供了需要的文件夹、项模板和配置文件入口。

【实例18-1】MVC程序的创建

本实例在Vistual Studio 2010开发环境中创建一个的基于ASP.NET MVC框架的Web应用程序并运行结果,具体实现步骤如下:

01 启动Vistual Studio 2012,选择如图18-2所示菜单栏上的“文件”|“新建项目”命令,弹出如图18-3所示的“新建项目”对话框。

alt

图18-2 新建项目

alt

图18-3 “新建项目”对话框

02 选择“已安装”模板下的“Visual C#”模板中的Web选项,并在模板文件列表中选中“ASP.NET MVC 4 Web应用程序”,然后在“名称”和“解决方案名称”文本框输入MvcApplication1,在“位置”文本框中输入保存文件的目录,最后单击“确定”按钮,弹出如图18-4所示的“新ASP.NET MVC 4项目”对话框。

alt

图18-4 “新ASP.NET MVC 4项目”对话框

03 在“选择模板”列表框中列出了可以选择的模板:

● 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开发人员,开发人员希望按照自己的需要设置和配置应用程序。

● 空(Empty)模板,在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项目类型中。

在“视图引擎”下拉列表中提供了在MVC应用程序中不同模板语言来生成HTML标记,包括ASPX和Razor两种视图引擎。

所有内置项目模板都提供选项创建一个单元测试项目,并提供了示例单元测试。如果不选中“创建单元测试项目”复选框,则项目不会创建任何单元测试。在选择“创建单元测试项目”复选框后,可以看到如下选择:

● 单元测试项目的名称,可以修改;

● 选择测试框架,默认只有一个测试框架选项,如果按照其他单元测试框架,如xUnit、NUnit、MbUnit等等,就可以在下拉列表中看到了。

这里选择其中的“Internet应用程序”选项,在“视图引擎”下拉列表中选择Razor选项,使用默认的Razor试图引擎,不选中“创建单元测试项目”复选框,然后单击“确定”按钮。

04 此时,在“解决方案资源管理器”中会自动生成一个如图18-5所示的基本ASP.NET MVC 4的Web应用项目MvcApplication1。

alt

图18-5 ASP.NET MVC应用程序

05 按快捷键Ctrl+F5运行程序,效果如图18-6所示。程序显示了MvcApplication1项目首页。细心的读者会问还没有编写任何的代码,怎么就会出现设计好的页面呢。其实在Visual Studio 2012开发环境中创建的每一个ASP.NET MVC项目都是一个模板也就是一个示例,为开发基于MVC设计模式的Web应用程序提供帮助。

alt

图18-6 程序首页

18.2.2 MVC应用程序的结构

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

alt

图18-7 网站目录结构

从网站目录结构图中可以看到,在新创建的网站项目中包含了很多自动生成的文件夹和文件,有些在创建其他类型的网站项目中已经介绍过,有些则是第一次看到的。为了能够方便代码的管理,利用ASP.NET MVC框架创建出的网站项目会自动生成这些文件夹和文件。

● App_Data文件夹:用来存储可读写数据文件,与基于Web表单的ASP.NET Web应用程序中的App_Data文件夹具有相同的功能。

● App_Start文件夹:存放功能配置代码,如Routing、Bundling、WebAPI等等。

● Content文件夹:存放CSS和其他非Scripts和图像的网站内容。

● Controllers文件夹:存放负责处理URL请求的控制器类,控制器组件一般存放在Controllers文件夹中,控制器的命名约定采用XXXController的方式。

● Filters:存放过滤器代码,过滤器是ASP.NET MVC中的一个高级特性。

● Images:存放网站中使用到的图像文件。

● Models文件夹:模型组件一般存放在Models文件夹中,例如LINQ to SQL类或者ADO.NET Entity Data Model就可以存放在该目录中。该目录还可以存放有关数据访问操作的一些类和对象的定义等。

● Scripts文件夹:存放JavaScript类库文件和脚本文件.js。

● Views文件夹:视图组件一般存放在Views文件夹中,可以存放的类型包括.aspx页面、.ascx控件及.master母版页等。这里需要说明的是对每一个控制器,在View文件夹中都有一个与控制器对应的目录。例如,存在一个控制器HomeController,那么在Views文件夹中,就必须创建一个Home(控制器HomeController名称的前面部分)的目录,这样当ASP.NET MVC框架通过控制器HomeController加载相关的视图时,就会自动寻找Views/Home目录下相关的.aspx页面。

除了上面的一些一级文件夹,还有两个比较重要的目录。这个只是ASP.NET MVC 4的默认项目结构,开发人员可以根据实际需要进行调整。如在大型应用程序中,一般使用多个项目,如将数据模型类存放在一个独立的Class Library项目中,让项目更易于管理。然而,默认的项目结构提供了一个很好的默认目录约定,使应用程序的关注点很清晰。

除了默认的目录结构之外,Visual Studio 2012还创建了一些默认的文件:如在/Controllers目录下,有两个控制器(Controller)类,分别为HomeController和AccountController;在/Views目录下,有三个子目录,分别为/Account、/Home和/Shared,以及一些模板文件;还有/Content和/Scripts目录,Site.css文件用来设置站点的HTML样式,JavaScript类库则让应用程序支持jQuery应用;在/View目录下,有一个Shared的文件夹,该目录不属于单个的控制器,而是属于所有的控制器,在Shared中可以存放母版页、CSS样式表等文件。

另外,Global.asax这一文件非常的重要,因为在ASP.NET MVC中,使用了Global.asax文件中的后置代码文件Global.asax.cs,在它里面默认生成了相关的路由逻辑。打开该文件可以看到如下的代码:

alt

以上代码中第1行定义了一个MvcApplication类继承于System.Web.HttpApplication。第4行在Application的开始事件Start中调用RegisterRoutes方法。当程序运行后,程序就会按照方法RegisterRoute定义的寻址功能来实现应用程序的寻址。

18.2.3 URL路由

在这一节中,介绍ASP.NET MVC应用程序的一个重要的特点叫做“URL路由”。URL路由负责映射从浏览器请求到特定的控制器动作。

1.定义URL路由

定义URL路由,就是设置URL模式。在URL路由中,通过大括号“{}”定义占位符,这些占位符就是URL路由的参数,而字符中的“/”、“.”等符号则被作为分隔符被URL路由解析这些离散的数据,对于不在小括号或者方括号中的信息则被视为一个常量。表18-1说明了如何定义URL路由。

表18-1 定义URL路由

alt

在表18-1中可以看到,第一行定义了含有三个URL路由参数的URL路由,此时例子中的Products就是控制器的名称,show就是该控制器中所定义的一个方法,而beverages则是一个id变量。对于第2行所定义的URL路由来说,例中的Products是一个数据表名称,而Details.aspx则是一个常量。第3行定义了一个含有两个URL路由参数的URL路由,此时例子中的blog是一个常量,show是相关控制器中所定义的一个方法,而123则是一个enty变量。第4行定义了含有4个URL路由参数的URL路由,此时sales是一个repotrtype变量,2008是一个year变量,1是一个month的变量,5是day的变量。因此,通过定义URL路由,非常有利于对相关页面功能的理解。

通常情况下,路由的添加是在文件Global.asax的Application_Start事件处理器函数中进行的,这样可以确保当应用程序启动时路由是可用的,并且在对应用程序进行单元测试时还支持直接调用该方法。如果想在单元测试应用程序时直接调用它,那么,必须把注册路由的方法设置为静态的并且为其提供一个参数RouteCollection。

一般是通过把各个路由添加到RouteTable类的静态Routes属性中实现最终添加路由的。其中,属性Routes是一个RouteCollection对象集合,其中存储了ASP.NET应用程序所有的路由。

下面代码展示了来自于文件Global.asax中的代码片断,在代码中添加一个Route对象,此对象中定义了两个名字分别为action和categoryName的URL参数。

alt

在上述代码中,第1行定义处理Application对象开始事件Start的方法,调用下面定义的RegisterRoutes方法。第2行调用第4行所定义的RegisterRoutes方法,在该方法中定义了一个URL路由,其中定义了两个URL路由参数,它们分别是Action变量和categoryName。当URL路由处理URL请求时,首先去寻找匹配的URL路由,例如被请求的URL为http://server/application/Category/Show/Tools时,根据代码中所定义的URL路由,被请求的URL是匹配该URL路由的,因此URL路由参数中的Action变量为对应控制器中的Show方法,categoryName变量则为Tools。如果被请求的URL为http://server/application/Category/Add,此时该URL与代码中所定义的URL路由不匹配,并且也没有定义默认的URL路由,那么此时的URL路由将不处理该请求,这个URL请求被当做普通的页面由传统的ASP.NET应用程序处理。

2.默认的URL路由

通过ASP.NET MVC项目模板所建立的一个基本MVC网站,在Global.asax文件中就已经设定了默认的URL路由,以便我们即刻运行所建立的MVC网站。表18-2说明了所设定的默认URL路由。

表18-2 ASP.NET MVC支持的默认的URL模式

alt

从表18-2中可以看到,第1行设定了URL路由应当包括三个部分的路径参数,对于右边匹配的URL来说,对应的控制器名称为ProductsController,控制器ProductsController中的执行方法为show,而id变量则为beverages。第二行设定了默认的URL路由为Default.aspx页面,该页面所对应的URL为http://server/application/Default.aspx。

在设定了默认路径之后,如果被请求的URL没有包括相关的URL路由参数,那么ASP.NET MVC框架会设置默认的URL路由参数,表18-3说明了如何使用默认的URL路由参数。

表18-3 对应两种模式默认的URL路由参数

alt

从表18-3中可以看到,如果使用第1行所定义的URL路由,那么,默认的执行方法为Index,而id变量则为null。如果被请求的页面是第二行的Default.aspx,那么ASP.NET MVC框架使用默认的控制器为HomeController,默认的执行方法为Index,而id变量则为null。

3.设定URL路由参数的默认值

当定义好一个路由后,可以把一个默认的值赋给一个参数。如果URL中没有提供此参数值,那么将使用此默认值。为一个路由设置默认值,可以通过把一个字典赋值给Route类的Defaults属性来实现。

下面代码演示了如何为URL路由的参数设置默认值:

alt

以上代码中,第5行~第10行创建了带两个参数action和categoryName的URL路由。第12行~第14行为创建好的URL路由中的参数设置默认值,即categoryNam变量的默认值是food,而Action方法则是对应控制器中的show方法。表18-4说明如何使用URL路由参数的默认值。

表18-4 URL路由参数的默认值

alt

从表18-4中可以看到,第1行被请求的的URL中没有包括任何的URL路由参数,因此URL路由将使用设定的默认值,此时categoryName变量的默认值是food,而Action方法则是对应控制器中的show方法。第2行中被请求的URL中包括一个URL路由参数,因此URL路由解析该URL后,此时categoryName变量的默认值是food。而Action方法则是对应控制器中的add方法。第3行中被请求的URL中包括完整的URL路由参数,因此URL路由解析该URL后,此时categoryName变量的默认值是beverages。而Action方法则是对应控制器中的add方法。

4.使用URL路由

在ASP.NET 4.5中,包括了一个命名空间System.Web.Routing,该程序集下的各个类主要实现路由的定义、解析、匹配等功能。也就是说,路由并不是专门服务于ASP.NET MVC框架,它同样可以运用在WebForm程序中。

(1)Route类

Route类是抽象类RouteBase的子类,在Route类中,设置了路由中的5个基本属性,它们分别是路由的约束Constraints、路由的命名空间Constraints、路由参数的默认值Defaults、路由处理程序RouteHandler和路由URL。Route类还定义了4个重载的构造函数,如表18-5所示。

表18-5 Route类的构造函数列表

alt

从表18-5中可以看出,在最简单的构造函数中,需要输入URL路由和路由处理程序两个参数;在最复杂的构造函数中,则需要输入Route类中的5个基本属性。

以下示例说明如何使用包括Route类中5个基本属性的构造函数:

alt

以上代码分别定义:一个路由"Archive/{entryDate}"、一个URL路由参数的默认值new RouteValueDictionary{{"controller","Blog"},{"action","Archive"}}、一个利用正则表达式定义输入参数entryDate为指定日期格式的约束new RouteValueDictionary{{"entryDate",@"\d{2}-d{2}-\d{4}"}}、一个定义命名空间的new RouteValueDictionary {{"namespace","Spencer.Route"}}以及路由处理程序new MvcRouteHander()。

(2)RouteCollection类

在实际的路由运用中,有时候需要创建多个路由,而RouteCollection类就是用来管理这些路由集合的。通过RouteTable类的静态属性Routes可以获得RouteCollection类的实例化对象。利用这一特性,可以在Global.asax文件中设置多个路由,设置的代码如下:

alt

以上代码中,第2行中的方法参数使用了RouteTable.Routes属性,以便获得RouteCollection类的实例化对象,然后通过第5行、第10行RouteCollection类的Add方法在集合类中添加新的路由。

(3)MapRoute扩展方法

添加路由最简单的方法是使用位于命名空间System.Web.Mvc中的RouteCollectionExtension静态类,在这个类中针对路由集合类RouteCollection扩展了二个方法,他们分别是IgnoreRoute方法和MapRoute方法:IgnoreRoute()方法主要用于设置不需要使用路由解析的URL地址,有二个重载的方法。MapRoute()方法则用于设置各种的路径,一共有6个重载的方法。RouteCollectionExtension类的扩展方法列表如表18-6所示。

表18-6 RouteCollectionExtension类的扩展方法

alt

(续表)

alt

从表18-6中可以看出,各种扩展方法中的输入参数仍然是Route类中的5个基本属性,不过通过定义新的扩展方法,这些基本属性的变量类型有所改变,以便开发者可以更加方便地设置这些属性。如路由的默认值由原来的RouteValueDictionary类型,改变为现在的object类型;命名空间也由原来的RouteValueDictionary类型,改变为现有的string[]类型。

【实例18-2】路由的使用

本实例演示如何在ASP.NET MVC项目中添加一个.aspx页面的路由,并由路由解析该页面。具体实现步骤如下:

01 启动Vistual Studio 2012,创建一个基于ASP.NET MVC 4的Web应用项目“实例18-2”。

02 在应用程序根目录下添加一个名为WebForm1.aspx的窗体文件。

03 单击网站根目录下的App_Start文件夹中的RouteConfig.cs文件,在文件中编写代码如下:

alt

上面的代码中,第1行定义了静态方法RegisterRoutes添加路由,参数是RouteCollection类的对象routes。第2行使用RouteCollection类对象routes的IgnoreRoute方法设置不需要使用路由解析的URL地址。第3行~第7行使用routes对象的MapRoute方法添加了WebForm1.asxp页面的路由。其中第4行的Start是URL路由的名称。第5行设置具体的路由。第6行设置路由的参数。第8行~第12行使用routes对象的MapRoute方法添加网站默认的路由,同样使用了三个参数的方法。

04 单击网站根目录下的Global.asax文件夹中的Global.asax.cs文件,在文件中编写代码如下:

alt

上面的代码中第1行~第8行定义处理Application对象开始事件Start的方法,其中,最关键的是第7行调用RouteConfig.cs文件中定义的RegisterRoutes的注册路由的方法RegisterRoutes。第8行设置了RouteExistingFiles的属性为true,表示ASP.NET MVC框架中的路由将要解析被请求的Default.aspx页面,如果设置了RouteExistingFiles的属性为false(RouteExistingFiles属性的默认值)表示ASP.NET MVC框架中的路由并不处理MVC网站中现有的Web文件页面,也就是说,在默认情况下,路由没有解析Default.aspx页面,把该页面当做普通WebFom页面来执行。

05 按快捷键Ctrl+F5运行程序,效果如图18-6所示。由于路由没有解析Default.aspx页面,而是找到匹配的路由Start,打开指定视图的Index.aspx页面。

18.2.4 MVC应用程序的执行过程

当请求一个ASP.NET MVC应用程序时,请求首先要传递到UrlRoutingModule对象,这个对象是一个HTTP模块,它解析请求并执行路由选择。UrlRoutingModule对象选择第一个与当前请求匹配的路由对象,路由对象通常是Route类的实例,并实现RouteBase基类。如果没有匹配的路由,UrlRoutingModule对象将不会做任何事情,并让请求返回到常规的ASP.NET或IIS请求处理程序中。

UrlRoutingModule对象从被选择Route对象中获取IRouteHandler对象。通常,在一个MVC应用程序中,IRouteHandler对象将会是MvcRouteHandler类的一个实例。IRouteHandler实例创建一个IHttpHandler对象并把它传递给IHttpContext对象。默认情况下,IHttpHandler实例是一个MvcHandler对象。接着,MvcHandler对象选择能够处理请求的控制器。

以上模块和处理器是进入ASP.NETMVC框架的入口,进入MVC框架后将执行如下的行为:

● 选择适当的控制器。

● 获得指定的控制器实例。

● 调用控制器的可执行方法。

总之,在一个MVC Web项目执行过程中,将经历如下几个阶段:

(1)获取第一个请求。在Global.asax文件中,Route对象被添加到RouteTable对象中。

(2)执行路由。UrlRoutingModule对象使用RouteTable集合中第一个匹配的Route对象以创建RouteData对象,利用这个对象以生成RequestContext对象(IHttpContext对象)。

(3)创建MVC请求处理。MvcRouteHandler对象创建一个MvcHandler类的实例,并把它传递到RequestContext实例。

(4)创建控制器。MvcHandler对象使用RequestContext实例去确认IControllerFactory对象以创建控制器实例。

(5)执行控制器。MvcHandler实例调用控制器的可执行方法。

(6)触发行为。很多控制器都继承自Controller基础类,而同控制器结合在一起的ControllerActionInvoker对象来决定控制器类调用哪个方法并调用。

(7)执行结果。一个典型的行为方法可能接收用户输入,准备适当响应数据,并通过返回一个结果类型来执行结果。可被执行的内置的结果类型包括ViewResult(用来渲染视图,并且是最常用的结果类型)、RedirectToRouteResult、RedirectResult、ContentResult、JsonResult和EmptyResult。

18.2.5 构建模型

在ASP.NET MVC框架中,模型主要实现应用程序中数据访问和业务逻辑,按照规定,这些模型类均存放在Models文件夹中。可以使用各种各样不同的技术来实现数据访问和业务逻辑。比如Microsoft Entity Framework、NHibernate、Subsonic或者ADO.NET类来构建的数据访问类。当然,最为常用的实体数据模型(ADO.NET Entity Data Model)。下面以ADO.NET实体数据模型为例来实现ASP.NET MVC程序中模型的创建。

【实例18-3】构件模型

本实例在ASP.NET MVC 4 Web应用程序的Models文件夹中构建在第6章中创建的db_news数据库和tb_News数据表的实体数据模型,具体实现步骤如下:

01 打开Vistual Studio 2012,创建一个名为MvcApplication3的ASP.NET MVC4 Web应用程序。

02 在“解决方案资源管理”窗口中的MvcApplication3项目内的Models文件夹上单击鼠标右键,在弹出的快捷菜单中选择“添加”|“新建项”命令,打开的“添加新项”对话框。

03 选择“已安装模板”下的“Visual C#”,并在模板文件列表中选中“ADO.NET实体数据模型”,然后在“名称”文本框输入Model1.edmx,最后单击“添加”按钮,弹出“实体数据模型向导”对话框。

04 在“模型将包含哪些内容”列表中选择“从数据库生成”选项,单击“下一步”按钮,进入“选择您的数据连接”对话框。

05 单击“新建连接”按钮,弹出“连接属性”对话框。

06 在“数据源”文本框中输入Microsoft SQL Server(SqlClient),在“服务器名”下拉列表中选择SQL Server服务器的名称,选择“选择或输入数据库名称”单选按钮,在其下的下拉列表中选中db_news数据库的名称,最后单击“确定”按钮,返回“实体数据模型向导”对话框。

07 选中“将Web.Config中的实体连接设置另存为”多选框,单击“下一步”按钮,弹出“选择您的数据库对象和设置”对话框。

08 展开“表”|dbo,选择tb_News数据表,最后单击“完成”按钮。

09 此时在网站根目录下的Models会自动生成一些如图18-8文件,将其中的Model1.Context.tt和Model1.tt从程序中删除,剩下的是一个包含模型信息的Model1.edmx文件。该文件由实体设计器使用,通过该设计器可以以图形方式查看和编辑概念模型和映射。此外,实体数据模型设计器还会创建一个源代码文件Model1.Designer.cs,其中包含基于.edmx文件的CSDL内容而生成的类。该源代码文件是自动生成的,并在.edmx文件发生更改时随之更新。

alt

图18-8 生成的文件

10 单击Model1.edmx文件,出现如图18-9所示“实体数据模型设计器”界面,可以看到可视化的tb_News数据表。使用实体设计器可以直观地创建和修改实体、关联、映射和继承关系,还可以验证.edmx文件。

alt

图18-9 实体模型设计器

11 在“实体数据模型设计器”界面的空白处单击右键,在弹出的快捷菜单中选择“属性”命令,弹出实体属性模型“属性”窗口。

12 将“代码生成”属性组下的“代码生成策略”的属性值从“无”修改为“默认值”。至此,ASP.NET MVC 4 Web应用程序中的实体数据模型就创建完毕了。

18.2.6 控制器

MVC控制器负责响应对ASP.NET MVC网站发起的请求,每个浏览器请求都将被映射到一个专门的控制器。

1.控制器类

所有控制器的基类都是Controller类,这个类提供通用的ASP.NET MVC处理功能。Controller类实现了IController、IActionFilter和IDisposable接口。

Controller基类负责以下处理阶段:

● 定位适当的行为方法。

● 获取行为方法参数的值。

● 处理在执行行为方法过程中可能出现的所有错误。

● 提供默认的WebFormViewFactory类以用来渲染ASP.NET页面类型(视图)。

ASP.NET MVC框架默认在项目的Controller文件夹下创建HomeController.cs类文件来实现Home视图的控制器,下面的代码展示了HomeController控制器类的定义:

alt

在上面的代码中,第1行定义了控制器HomeController,所有控制器的名称命名必须形如XXXController的格式,并且必须实现接口IController或者继承抽象类Controller类。第2行定义了一个动作方法Index,该方法的返回类型是ActionResult。第3行设置了dynamic类型对象ViewBig的Message属性的内容,以便将控制器中指定的数据传递到视图。第4行调用Controller类中的View方法,返回的是一个ViewResult的实例化对象,将指定的内容输出到浏览器中。第6行和第10行定义了另外两个行为方法About和Contact,该方法的返回类型也是是ActionResult。

ActionResult是一个抽象类,因此实际返回的类型是该抽象类的子类。ActionResult的子类列表见表18-7所示。

表18-7 ActionResult的子类列表

alt

2.行为方法

在不使用MVC框架的ASP.NET应用程序中,用户交互都是围绕着页面以及引发和处理这些页面的事件进行组织的。相比之下,使用ASP.NET MVC应用程序的用户交互则围绕控制器及其中的行为方法进行组织。

行为方法是在控制器中定义的。通常,行为中针对每一个用户的交互都会创建一一对应的映射,用户的交互包括在浏览器中输入一个URL,单击一个链接,以及提交一个表单,等等。每一个此类用户交互都会把一个请求发送到服务器。而请求URL中都会包括相应的信息以便MVC框架来调用一个相应的行为方法。

例如,当用户在浏览器输入一个URL时,MVC应用程序使用定义于Global.asax文件中的路由规则来分析该URL并决定指向控制器的路径。然后,该控制器定位适当的行为方法来处理这一请求。根据具体需要,控制器中可以定义尽可能多的行为方法。

默认情况下,一个请求URL被当作一个子路径被解析,其中包括控制器名,后面跟着行为名。例如,一个用户输入URLhttp://contoso.com/MyWebSite/Products/Categories,则子路径为“/Products/Categories”。默认的路由规则总是把Products作为控制器名,而把Categories作为行为名。于是,该路由规则将调用Products控制器的Categories方法来处理该请求。如果URL以“/Products/Detail/5”结尾,则默认的路由规则把Detail作为行为名,并且调用Products控制器的Detail方法来处理请求。默认情况下,URL中的5将被传递为Detail方法的一个参数。

下面这段代码定义了一个控制器,并在该控制器中定义一个行为方法:

alt

以上代码中第2行定义了一个名为Hello的行为方法,它的返回类型是ActionResult抽象类的一个子类。

在Controller类中的相关方法和返回对象的列表如表18-8所示。

表18-8 控制器中的方法与返回对象列表

alt

在定义控制器中的行为方法时要留意,因为ASP.NET MVC框架认为所有的public方法都是行为方法。所以如果控制器类包含一个不是public的行为方法,那么必须使用NonActionAttribute属性标记它。

在ASP.NET MVC应用程序中创建一个控制器可以使用以下步骤:

01 用鼠标右键单击解决资源管理器中的Controllers文件夹,弹出如图18-10所示的快捷菜单。

alt

图18-10 快捷菜单

02 选择“添加”|“控制器”菜单项,弹出如图18-11所示的“添加控制器”对话框。

alt

图18-11 “添加控制器”对话框

03 在“控制器名称”文本框中输入控制器的名称,在“模板”下拉列表中列出了可供选择的多种模板,可以根据项目的实际需要进行选择,同样,在“模型类”和“数据上下文类”两个下拉列表中也列出了各自的候选项供用户选择。通常,在“模板”下拉列表框中选择“包含读/写操作和视图的MVC控制器(使用Entity Framework)”。在“模型类”下拉列表框中选择实体数据模型中的模型类,在“数据上下文类”下拉列表框中选择实体数据模型中创建的上下文类。例如前面创建的实体数据模型,在“模型类”下拉列表框中应选择tb_News(MvcApplication3.Models);在“数据上下文类”下拉列表框中应选择db_newsEntities(MvcApplication3.Models),最后单击“添加”按钮。

04 在“解决方案资源管理器”中的Controllers文件夹下自动生成如图18-12所示的控制器文件NewsController.cs文件。

alt

图18-12 控制器文件

然后,就可以在该文件中编写控制器的代码了。

【实例18-4】创建控制器

本实例为上例创建好的tb_News数据表的实体数据模建立控制器实现对该数据表的显示、编辑、添加、删除和查询详情的业务逻辑,在控制器中创建相关的动作方法Index、Edit、Create、Details和Delete,具体实现步骤如下:

01 打开Vistual Studio 2012,创建一个名为MvcApplication4的ASP.NET MVC4 Web应用程序。

02 在该项目中创建一个ADO.NET实体数据模型,命名为db_newsEntities。

03 单击项目根目录下的Controllers文件夹中的HomeController.cs文件,在文件中编写如下代码:

alt

上面的代码中第1行定义Home视图控制器类HomeController并继承于Controller抽象类。第2行实例化实体模型类db_newsEntities的db对象。第3行定义动作方法Index,对应的视图是Index.cshtml。第4行调用bs的tb_News对象的ToList方法获得全部的新闻信息,调用控制器的View方法将获得的信息传递到对应的Index视图显示。

第6行定义动作方法Details,对应的视图是Details.cshtml,方法的参数是新闻编号id。第7行获得数据表tb_News中指定新闻编号ID的新闻实体对象。第8行判断如果该对象为空,则第9行返回定义一个用于指示未找到所请求资源的对象的方法HttpNotFound,否则,第11行调用控制器的View方法获得实体对象传递到对应的Details视图显示。

第13行定义动作方法Create,对应的视图为Create.cshtml。第16行设置属性[HttpPost],表示下面的方法只接受用户通过Post方法发送表单数据。第17行定义一个重载的Create动作方法,对应的视图为Create.cshtml,方法的参数是一个要创建的新闻实体类对象。第18行判断如果模型和模型绑定状态通过验证,则第19行调用数据实体模型对象db的AddObject方法将新的对象添加到数据实体中。第20行调用SaveChanges方法将修改的数据保存到数据库。第21行程序跳转页面到Index视图。最后通过第23行将数据传递到对应的Index视图页面中显示。

第25行定义动作方法Edit,对应的视图是Edit.cshtml,方法的参数是新闻的编号id。第26行调用实体类对象tb_News的Single方法获得tb_News数据表中指定新闻编号的实体对象。第27行判断如果该对象为空,则第28行返回定义一个用于指示未找到所请求资源的对象的方法HttpNotFound,否则,第30行调用控制器的View方法将获得的实体对象传递到对应的Edit视图显示。

第32行设置[HttpPost]属性表示下面的方法只接受用户通过Post方法发的送表单数据。第33行定义了一个重载的Edit动作方法,对应的视图是Edit.cshtml,方法的参数是实体类新闻对象。第34行判断如果模型和模型绑定状态通过验证,则第35行调用数据实体模型对象db的Attach方法将新的对象附加到上下文对象实体集中。第36行调用db对象的ObjectStateManager.ChangeObjectState方法对实体对象的状态进行修改。第37行调用SaveChanges方法将修改的数据保存到数据库。第38行程序跳转页面到index视图。最后通过第40行将数据传递到对应的Index视图页面中显示。

第42行定义动作方法Delete,对应的视图Delete.cshtml,方法的参数是新闻编号id。第43行调用实体类对象tb_news的Single方法获得tb_News数据表中指定新闻编号的实体对象。第44行判断如果该对象为空,则第45行返回一个用于指示未找到所请求资源的对象的方法HttpNotFound,否则,第47行调用控制器的View方法将获得的实体对象传递到对应的Delete视图显示。

第49行设置属性[HttpPost, ActionName("Delete")]表示下面的方法只接受用户通过Post方法发送表单数据和执行动作的名称。第50行定义了一个重载的Delete动作方法,对应的视图是Delete.cshtml,方法的参数是新闻编号id。第51行调用实体类对象tb_news的Single方法获得tb_News数据表中指定新闻编号的实体对象。第52行调用db对象的DeleteObject方法删除获得的实体对象。第53行调用SaveChanges方法将修改的数据保存到数据库。通过第54行将数据传递到对应的Index视图页面中显示。

第50行定义了一个重写Dispose的方法,第51行调用db对象的Dispose方法释放对象上下文的资源。

18.2.7 视图

ASP.NET MVC框架提供一个视图引擎以生成视图。默认情况下,MVC框架使用定制的类型来生成视图,而这个类型继承自已经存在的ASP.NET页面(.aspx)、母版页(.master)和用户控件(.ascx)。在一个ASP.NET MVC Web应用程序的工作流程中,控制器行为方法处理收到的Web请求。这些行为方法使用收到的参数值来执行程序代码,并从数据库的模型中获取或更新数据。最后,他们选择一个视图来渲染用户界面。

ASP.NET MVC 4内置了两种常用的视图引擎:传统的ASPX视图引擎和Razor视图引擎。

1.ASPX视图引擎

ASPX视图引擎是ASP.NET Web窗体使用的.aspx/.ascx/.master文件模板。它可以追溯到遥远的ASP。使用“<%= %>”和“<%: %>”语法的占位符在这类风格中占据了统治地位。随着时间的推移,ASPC控件被加入进来,之后是母版页(Master Page),但这同时也带来了昂贵的页面生命周期。

2.Razor视图引擎

Razor视图引擎支持两种文件类型,分别是.cshtml和.vbhtml,其中.cshtml的服务器代码使用了C#的语法,.vbhtml的服务器代码使用了VB.net的语法。由此也可以看出,Razor其实是一种服务器代码和HTML代码混写的代码模板,类似于没有后置代码的.aspx文件。因为Razor使用了现有的VB或C#语法,微软预计它将很容易学习。任何文本编辑器都可以用来编辑Razor文件,而Visual Studio 2012也将更新加入对Razor文件智能提示的完整支持。

Razor的另一个重要特点是它与单元测试框架的兼容性。Razor模板不需要Controller或Web服务器作为宿主(host),所以用它写出来的视图应该是充分可测的。对于ASPX,虽然理论上一切皆可测试,但实际上却是相当困难。

一般情况下,在基于MVC的Web工程构架中,推荐把视图全部放到Views文件夹的下面。根据MVC框架要求,视图中不应该包含任何应用程序逻辑或数据库检索代码。所有的应用程序逻辑都应该由控制器来负责处理。

借助于控制器行为方法提供的与MVC视图相关的数据对象,由视图负责渲染相应的用户接口界面。

视图页面是一个ViewPage类,这个类继承自Page类,并实现IViewDataContainer接口。IViewDataContainer接口提供了一个ViewData属性,这个属性返回一个ViewDataDictionary对象,这个对象包含视图要显示的数据。

可以使用ASP.NET MVC Web应用程序项目中提供的模板来创建视图,MVC框架利用URL路由来决定调用哪个控制器行为,而控制器行为决定要渲染哪个视图。

【实例18-5】创建视图

本实例在上例的基础上,为HomeController控制器的各种行为方法创建相关的视图页面,具体实现步骤如下:

01 删除解决资源管理器中Views文件夹下Home子文件夹下的Index.cshtml文件。

02 单击解决资源管理器中Controllers文件夹下的HomeController.cs文件,打开HomeController控制器,选中Index()方法,然后单击鼠标右键,弹出如图18-13所示的快捷菜单。

alt

图18-13 添加视图

03 选择“添加视图”命令,打开如图18-14所示的“添加视图”对话框。在“视图引擎”下拉列表中选择Razor(CSHTML);选中“创建强类型视图”多选按钮,之所以要选择这个,是因为是通过实体数据模型来创建数据访问的;在“视图数据类”下拉列表中找到实体数据类tb_News(MvcApplication4.Modes);在“支架模板”下拉列表中选择List列表,在这个下拉列表中有6个选项,每一个选项就是一个视图的模板:Create选项用于创建新建数据的视图、Delete选项用于创建删除数据的视图、Details选项用于创建单条数据详情的视图、Edit选项用于创建编辑数据的视图。Empty选项用于创建一个空的模板视图、List选项用于创建显示数据的视图。选中“引用脚本库”复选按钮;然后选中“选择母版页”多选按钮,在下面的文本框中输入母版页的路径。如果不使用母版页这些都可以不选,最后单击“添加”按钮。

alt

图18-14 “添加视图”对话框

04 ASP.NET MVC 4框架就会自动的在如图18-15所示的网站根目录Views文件夹下的Home子文件夹下创建一个Index.cshtml视图页面。

alt

图18-15 生成视图文件

05 单击Index.cshtml文件,在文件中生成的实现代码如下:

alt

在上面的代码中,第1行在页面引用实体数据模型中的实体类tb_News作为一个枚举类型的集合。第3行设置ViewBag.Title属性设置页面的标题。第4行设置LayoutPage属性,它指明了我们期望用SiteLayout.cshtml作为这个视图的版面设计模板。第7行设置“添加新闻”链接,用于添加新的新闻数据。第8行~第45行设置了一个表格来显示数据表tb_News的数据。其中,第9行~第23行设置了表格的第一行,分为4列。第24行~第44行通过常用的foreach循环语句,在从控制器传递到视图的Model数据中,分别读取tb_News数据表中的Title字段(第11行)、Categories字段(第30行)、Type字段(第33行)和IssueDate字段(第36行)。其中第29行~第41行使用了HTML中的扩展方法ActionLink,设置“编辑”、“详情”和“删除”链接,用于编辑指定记录或查看该记录的详细信息。

06 视图Index.cshtml的运行界面如图18-16所示。用户单击“编辑”按钮,就会进入编辑新闻的页面;如果单击“详情”按钮,可以进入新闻详情页面;如果单击“删除”按钮将进入删除新闻的页面;单击“添加新闻”链接将进入添加新闻的页面。

alt

图18-16 Index视图运行结果

07 选择控制器HomeController中的Edit()方法,然后单击鼠标右键,在弹出的快捷菜单中选择“添加视图”命令,打开如图18-17所示的“添加视图”对话框。在“视图引擎”下拉列表中选择Razor(CSHTML);选中“创建强类型视图”多选框;在“模型类”下拉列表中找到实体数据类tb_News(MvcApplication4.Modes);在“支架模板”下拉列表中选择Edit列表;选中“引用脚本库”复选按钮;然后选中“使用布局或母版页”多选按钮,在下面的文本框中输入母版页的路径;最后单击“添加”按钮。ASP.NET MVC 4就会自动的在网站根目录Views文件夹下的Home子文件夹下创建Edit.cshtml页面。

alt

图18-17 “添加视图”对话框

08 单击Edit.cshtml文件,在文件中生成的实现代码如下:

alt

上面的代码中第7行引入Html的BeginForm方法定义表单的开始部分。第8行设置一个验证摘要信息控件ValidationSummary,当表单的文本框中含有空白输入时,该控件就会在表单页面中输入指定的错误信息。第9行~第48行设置了一个边框,边框内分别显示了数据表tb_News中的Title标签和字段、Categories标签和字段、Type标签和字段以及IssueDate标签和字段,第47行设置了提交请求的“保存”按钮。在边框下面,第51行还设置了一个“返回新闻列表”的链接,单击它会返回到Index.cshtml视图页面。

09 视图Edit. cshtml页面的运行界面如图18-18所示,用户可以选择修改新闻的各项具体信息,然后单击“保存”按钮,就会保存修改的内容并打开前面所示的Index.cshtml页面。

alt

图18-18 Edit视图运行结果

10 选择控制器HomeController中的Create()方法,然后单击鼠标右键,在弹出的快捷菜单中选择“添加视图”命令,打开“添加视图”对话框,在“视图引擎”下拉列表中选择Razor(CSHTML);选中“创建强类型视图”多选按钮;在“视图数据类”下拉列表中找到实体数据类tb_News(MvcApplication4.Modes);在“支架模板”下拉列表中选择Create列表;选中“引用脚本库”复选按钮;然后选中“选择母版页”多选按钮,在下面的文本框中输入母版页的路径;最后单击“添加”按钮。ASP.NET MVC 4就会自动的在网站根目录Views文件夹下的Home子文件夹下创建Create.cshtml页面。

11 Create. cshtml文件中生成的实现代码与Edit. cshtml极其类似这里就不再重复介绍。

12 视图Create.cshtml页面的运行界面如图18-19所示。用户在文本框中输入新闻的各项具体内容,然后单击“新建”按钮,就会添加一条新的新闻数据并打开前面的Index. cshtml页面。

alt

图18-19 Create视图运行结果

13 选择控制器HomeController中的Details()方法,然后单击鼠标右键,在弹出的快捷菜单中选择“添加视图”命令,在“视图引擎”下拉列表中选择Razor(CSHTML);选中“创建强类型视图”多选按钮;在“视图数据类”下拉列表中找到实体数据类tb_News(MvcApplication4.Modes);在“支架模板”下拉列表中选择Details列表;选中“引用脚本库”复选按钮;然后选中“选择母版页”多选按钮,在下面的文本框中输入母版页的路径;最后单击“添加”按钮。ASP.NET MVC 4就会自动地在网站根目录Views文件夹下的Home子文件夹下创建一个Details.cshtml页面。

14 视图Details.cshtml页面的运行界面如图18-20所示。用户单击“编辑”链接能够回到前面的Edit.cshtml页面。单击“回到列表”链接,可以回到前面的Index.cshtml页面。

alt

图18-20 Detail.aspx运行结果

15 选择控制器HomeController中的Delete()方法,然后单击鼠标右键,在弹出的快捷菜单中选择“添加视图”命令,打开“添加视图”对话框,在“视图引擎”下拉列表中选择Razor(CSHTML);选中“创建强类型视图”多选按钮;在“视图数据类”下拉列表中找到实体数据类tb_News(MvcApplication4.Modes);在“支架模板”下拉列表中选择Delete列表;选中“引用脚本库”复选按钮;然后选中“选择母版页”多选按钮,在下面的文本框中输入母版页的路径;最后单击“添加”按钮。ASP.NET MVC 4就会自动的在网站根目录Views文件夹下的Home子文件夹下创建Delete.cshtml页面。

16 单击Detail.cshtml文件,在文件中生成的实现代码如下:

alt

以上代码中第8行~第40行设置了一个边框,边框内分别显示了数据表tb_News中4个字段的内容。第43行设置了提交请求的“删除”按钮。第44行设置了一个“返回新闻列表”的链接。

17 视图Delete.cshtml文件运行的结果如图18-21所示。单击“删除”按钮,将显示的该条数据从数据库表中删除并回到Index.cshtml页面。

alt

图18-21 Delete.cshtml运行结果

教程类别