[原文地址] [阅读该系列的其他文章]
前几周我发表了一系列文章介绍我们正在研究的
ASP.NET MVC
框架。
ASP.NET MVC
框架为你提供了一种新的开发
Web
应用程序的途径,这种途径可以让应用程序变得更加层次清晰,而且更加有利于对代码进行单元测试和支持
TDD
(测试驱动开发)开发。
这一些列的
第一篇文章
创建了一个简单的电子商务产品列表
/
浏览站点。他涉及到了
MVC
背后的高层次概念并演示了一个
ASP.NET MVC
项目从设计到实现的过程和对产品列表功能的测试。该系列的
第二篇文章
深入介绍了
ASP.NET MVC
框架的
URL
映射机制并针对其工作原理和更复杂
URL
映射的处理进行了深入讨论。
本文将要讨论控制类
(
英文名称
:Controller
,以下统一称
Controller)
如何与视图类
(
英文名称
Views
,以下统称
Views)
进行交互,并专门介绍从
Controller
到
Views
传递数据以便给客户端呈现内容的方式。
Part 1
回顾
该系列的
第一篇文章
我们采用
ASP.NET MVC
框架创建了一个电子商务站点,实现了基本的产品列表
/
浏览功能,并把代码自然的划分到不同的
Controller,
模型
(
英文名称:
Model
,以下统一称
Model)
和
View
部分。
当浏览器向服务器发送一个
HTTP
请求时,
ASP.NET MVC
框架将使用
URL映射引擎
将该请求映射到
controller
的操作方法来处理。
MVC
应用程序中的
Controller
处理客户端请求,捕获用户输入和进行交互,并执行相应的应用逻辑
(
获取或更新数据库中的模型数据等
)
。
当需要向客户端返回
HTML
回应时,
controller
针对
views
进行操作,
Views
是独立于
controller
的类或模板,并主要完成显示逻辑的封装。
Views
不应该包含任何应用逻辑或数据访问代码,所有的应用
/
数据逻辑都应该有
controller
来完成。这样做的好处是让应用
/
数据逻辑与
UI
呈现代码有更加清晰的分离,并使得针对应用
/
数据逻辑的单元测试与
UI
呈现逻辑相分离。
View
应当只根据由
Controller
针对该
View
传来的数据生成输出。在
ASP.NET MVC
框架中,我们把该数据成为视图数据
(ViewData)
。下面将要介绍将视图数据转换为输出的不同方式。
简单的产品列表场景
让我们常见一个产品列表页来说明我们从
Controller
向
View
传递视图数据的技术:
我们将要使用
CategoryID
整数来过滤出我们希望产生在页面上的产品。注意上面我们是如何把
CategoryID
作为
URL
的一部分的(例如
: /Products/Category/2
或
/Products/Category/4
)
我们的产品列表页要产生两个独立的动态内容元素,第一部分是我们要显示的目录名称
(
例如:
Condiments)
,另一部分是一组
HTMl <ul><li></li></ul>
标记显示产品名称列表。这两部分我都已经在上面的截图中作出红色标记。
下面我们将要分别介绍两种方式,来实现
ProductsController
类处理请求,获取数据并显示数据的过程。第一种方式我们要使用迟绑定
(late-bound)
对象,另一种方式我们要使用强类型类
(strong-typed)
。
方式
1
:使用
Controller.ViewData
传递视图数据
Controller
基类包含一个名为
”ViewData”
的属性可以用来存放传递给视图的数据,你可以使用
name/value
的形式向
ViewData
中添加对象。
下面的
ProductsController
类中包含一个
Category
方法可以实现上面所说的产品列表功能。注意该类是如何使用目录的
ID
参数来寻找该目录的名称的,以及如何获取该目录中的所有产品。这两部分内容都被存放在
Controller.ViewData
集合中,并分别使用
”CategoryName”
和
“Products”
名称作为主键:
上面的
Category
方法调用
RenderView(“List”)
并指明了将要使用哪个视图模板来产生输出,当调用
RenderView
方法时,
Controller
将把
ViewData
传递到
View
中以便进行显示。
实现
View
我们将使用
/Views/Products
目录下的
List.aspx
页面文件来实现
List
视图,该文件将继承
/Views/Shared
目录下
Site.Master
母版页的布局(在
VS2008
中创建新的视图时右单击项目选择
”
添加新项目
”->”MVC
视图内容页
”
选择母版页):
当我们使用
MVC
视图内容也模板创建
List.aspx
页时,并不是从
System.Web.UI.Page
类继承而来,而是继承自
System.Web.Mvc.ViewPage
基类(该类是
Page
类的子类):
ViewPage
基类同样为我们提供了
ViewData
属性,我们可以在
View
中使用该属性访问由
Controller
添加的对象,我们可以使用这些数据并通过服务器控件或
<%=%>
标记产生
HTML
输出。
使用服务器控件实现
View
在下面的例子里我们使用已有的
<asp:literal>
和
<asp:repeater>
服务器控件实现
HTML
输出:
我们可以采用如下方式通过后台代码类将视图数据绑定到这些控件(注意我们是如何使用视图页的
ViewData
属性来实现的):
注意:因为页面上没有
<form runat=”server”>
标记,因此不会生成视图状态
(ViewState).
上面的控件同样不会自动产生任何
ID
值,这意味着你将对
HTML
输出具备完全的控制。
通过
<%=%>
实现视图
如果你希望使用迁入代码的方式产生输出,你可以用下面的
List.aspx
页面实现与上面相同的效果:
注意:因为
ViewData
属性的类型为包含对象
(object)
的字典,我们需要将
ViewData[“Products”]
转换成
List<Product>
类型或
Ienumerable<Product>
类型来使用
foreach
语句。在页面上引用
System.Collections.Generic
和
MyStore.Models
命名空间避免在
List<T>
和
Product
类型中使用完整的类型名称。
注意:上面
”var”
关键字的使用是在
VS2008
中使用新的
C#
和
VB”Type inference
(字面意思为类型推理)
”
特性的例子
(
点击
这里
阅读相关文章
)
。因为我们已经将
ViewData[“Products”]
转换为
List<Product>
类型,在
List.aspx
文件中我们将获得
product
变量的智能感知支持。
方式
2
:使用强类型
(strong-typed)
类传递视图数据
除了对迟绑定数据源方式之外,
ASP.NET MVC
框架同时允许从
Controller
向
View
中传递强类型的视图数据对象。使用强类型的方式有如下优点:
- 避免使用字符串来搜索对象,同时对Controller和View代码都可以得到编译时错误检查
- 避免在使用强类型语言(例如C#)时对视图数据进行强制类型转换
- 在视图页的标记和后台代码中可以有代码只能感知支持
- 你可以使用代码反射工具在应用程序和单元测试代码范围内对代码进行自动修改
下面的
”ProductsListViewData”
强类型类封装了
List.aspx
视图中用户产品列表的数据,它具有
CategoryName
和
Products
属性(通过
C#
中新的
自动属性automatic property
支持实现):
修改
ProductsController
使用该对象将强类型的视图数据传递到视图中:
注意上面我们是向
RenderView()
方法中传入一个额外的参数将强类型的
ProductsListViewData
对象传递给视图的。
使用视图的
ViewData
属性访问强类型的
ViewData
对象
我们在前面实现的
List.aspx
视图不需要修改仍可以正常工作。这是因为当强类型的
ViewData
对象传入继承自
ViewPage
类的
View
时,
ViewData
属性将应用反射机制自动查找该对象的各种属性值,因此我们视图中的代码如下所示:
在
RenderView()
方法被调用是,上面的代码将会利用反射从我们传入的强类型
ProductsListViewData
对象中获取
CategoryName
属性。
对象类型
ViewData
使用
ViewPage<T>
基类
为了提供对
ViewPage
基类的支持,
ASP.NET MVC
框架同时包含了一个泛型基类
ViewPage<T>
,如果你的视图继承自
ViewPage<T>
,其中
T
代表
Controller
传入
View
的
ViewData
的类型,那么
ViewData
将被强类型话为指定的类型。
例如,我们可以修改
List.aspx.cs
文件使其继承自
ViewPage<ProductsListViewData>
而不是
ViewPage
基类:
此时该页的
ViewData
属性将自动定义为
ProductsListViewData
。这样我们可以使用强类型属性而不是字符串索引的方式获取数据:
然后我们可以使用服务器控件或
<%%>
的方式生成基于
ViewData
的
HTML
输出。
通过服务器控件实现
ViewPage<T>
在下面的例子中我们将使用
<asp:literal>
和
<asp:repeater>
服务器控件实现
HTML UI
,与前面继承自
ViewPage
基类的
List.aspx
页面相同:
后台代码如下所示。注意因为该类继承自
ViewPage<ProductsListViewData>,
所以我们可以直接访问属性而不需要任何转换(同时在任何时候我们希望修改属性名称时都可以得到反射工具的支持):
使用
<%%>
实现
ViewPage<T>
如果你希望使用嵌入代码的方式来生成输出,你可以使用下面的代码实现与上面相同的效果:
采用
ViewPage<T>
的方式我们避免使用字符串来检索视图数据,更重要的是属性成为强类型之后我们就不需要再进行属性的类型转换。这意味着我们可以直接使用
foreach (var product in ViewData.Products)
语句而不需要转换得到
Products
。在循环语句
product
变量将会有完全的智能感知支持:
总结
希望上面的文章已经对
Controller
向
View
传递用于呈现的数据的过程有了更加深入的介绍,你可以使用迟绑定的方式或强类型的方式来达到这一目的。
当你第一次创建一个
MVC
应用程序时,你或许会对将应用控制逻辑与表现代码相互分开的概念感到陌生,你需要一些时间来适应这种接受请求
-
执行应用逻辑
-
包装用于显示的视图数据
-
呈现的处理过程。重要:
MVC
方式为可选方式,如果你对这种开发模式并不习惯你可以不使用它。
这种方式的优点在于将运行和测试业务逻辑的过程与
UI
呈现分离,这是的开发单元测试更加简便,同时支持
TDD
开发模式。后面的文章我将深入介绍这一点,并讨论对代码进行测试的实践。
希望对你有所帮助,
Scott