Asp.Net MVC
本章将介绍ASP.NET MVC 的基础知识,帮助你了解ASP.NET MVC的轮廓。由于着重于观念介绍,如果觉得看不懂可先直接跳过,待后续的章节读完之后,再回头阅读本章也许更能帮助你创建正确的观念。
在学习ASP.NET MVC 之前
何谓MVC
在学习ASP.NET MVC 之前,需要先了解“什么是MVC?”。也许这对某些ASP/ASP.NET 开发人员来说非常陌生,MVC不是一种程序语言,严格说起来也不算是个技术,而是开发时所使用的一种“架构(框架)”。它就像是一种开发观念,或是一个存在已久的设计样式(Design Pattern)。
开发人员最熟悉的,在软件开发时最常发生的状况就是“变化”。需求会变、技术会变、老板会变、客户也会变、最惨的是PM也常在变。经常改变的需求,对于软件质量与可维护性有很大的杀伤力,但这是现实,也无法跳脱。我们唯一能做的,就是有效降低变化所带来的冲击,而MVC就是其中一种解决方案。
MVC最早是在1979 年由Trygve Reenskaug 所提出,并且应用于当时火红的Smalltalk 程序语言中。之所以会提出MVC的概念,主要的目的就在于简化软件开发的复杂度,以一种概念简单却又权责分明的架构,贯穿整个软件开发流程,通过“商业逻辑层1与“数据表现层”的切割,让这两部分的信息切割开来,用以撰写出更模块化、可维护性高的程序代码。
1我们在开发各种应用程序时,通常会依据客户的需求撰写程序逻辑,而大多数应用程序都是应用在“商业环境”里,例如,电子商务或支持企业营运的窗体应用程序,等等,因此各位常听到的“商业逻辑”讲的就是这些应商业环境所撰写的程序逻辑。如果你所开发的应用程序并非商业用途(例如学校单位),我们也可以通称这些依据需求所开发出来的逻辑为“商业逻辑”。商业逻辑层(Business Logic Layer,BLL)所包含的程序代码通常会包含信息格式定义(ORM)、信息访问程序、窗体的字段格式验证、信息保存的格式验证、数据流验证,等等。
MVC让软件开发的过程大致切割成三个主要单元,分别为:Model(模型)、View(检视)、Controller(控制器),而这三个单词的缩写便简称为MVC。其定义如图1-1所示。
图 1-1 MVC 的概略架构与分工
重点提示 为了阅读方便,本书后续的章节尽量不使用译名(模型、检视、控制器),而在大部分的情况下会以原名Model 、View 、Controller 来解说,以避免在中文语句上造成混淆。
何谓Model
Model 可翻译成“模型”,笔者认为译成“数据模型”会更贴切一些,因为Model 负责所有与“数据”有关的任务,大致如下。
定义数据结构。
负责与数据库沟通。
从数据库读取数据。
将数据写入数据库。
运行预储程序。
数据格式验证。
定义与验证商业逻辑规则。
对数据进行各种加工处理。例如:指定特定实体(Entity) 某些字段的默认值。
简言之,只要是和“数据”有关的任务,都应该在Model 里完成定义。
以.NET或Java平台开发经验来说,你可以想象Model 是一个命名空间(Namespace 或 Package) ,定义了一堆型别(Type)或类别(Class)来负责所有跟数据相关的工作。常见的相关技术包括ADO.NET、强型别数据集(Typed DataSet) 、Entity Framework 、LINQ to SQL、LINQ to SQL partial method 、数据访问层(Data Access Layer) 、Repository Pattern 。更详细的属性请参阅“第5章 Model 相关技术”。
何谓View
View可翻译成“检视”或者“视图”,但我很不喜欢这种为了翻译而翻译的名词,这样反而不利于沟通表达,所以之后一律会以View来表示。
重点提示 各位请不要将这里的View 和数据库系统(如SQL Server) 中的检视表(View) 混淆了,虽然是同一个英文单词,但在本书中只要提到View,讲的就是ASP.NET MVC 里面的View 喔!
View负责所有呈现在用户面前的东西,最简单的说法就是输出与输入。输出工作就是呈现在浏览器上的界面,例如,输出HTML、XML,等等。输入工作则是将用户输入的数据传回服务器,例如,在浏览器上呈现网页窗体让用户输入。
以下简单介绍,在View中与输出/输入有关的工作。
输出
从Controller 取得数据,并显示在用户界面上。
决定要用什么技术来呈现“用户界面”(例如,HTML、XML、Silverlight 、Flash,等)。
负责界面的排版、字型、颜色、美观与各种呈现方式。
将Controller 传送的数据显示于界面,而数据是参考自Model 的定义。
参考Model 的数据格式定义数据显示。
输入
负责将数据送回Controller 。
HTML 窗体通过GET 或POST 输出数据。
决定数据应该送到哪一个Controller 的Action 中。
决定数据传送的方式,例如,GET、POST、XML HTTP Request (XHR) 。
前端基本的数据格式验证。
验证功能,例如,使用JavaScript 验证表单域是否输入。
参考Model 的类别定义,在Visual Studio 中利用Intellisense 撰写程序。
此外,与View相关的演示与技术如下。
HTML / XML / CSS
JSON
JavaScript ( jQuery 、jQuery UI 、MooTools 、Ext2、Prototype… )
MasterPage
ASP.NET Controls ( Server Control 、User Control )
AJAX 相关技术
Silverlight
Flash
Mobile 网页(PDA、手机、iPhone…)
WAP 网页
iPhone 网页
简言之,所有应该要显示在网页上的逻辑都是View负责的范围。
随堂测验 现在,我们要将保存于数据库中的某文字字段属性显示到界面上。由于显示的属性会出现在View 内,且文字的多寡会导致View 的外观发生改变。请问,我们准备要显示于View 中的“文字属性”,是否为View 应该负责的范围?2
2解答:不是。因为取得数据是Model 的工作,而View 只负责将从Model 取得的文字数据“显示”在网页上。
何谓Controller
Controller 可翻译成“控制器”,顾名思义就是“掌控全局的对象”,其负责的工作如下。
决定与“用户”沟通的管道,以ASP.NET MVC 为例就是HTTP 或HTTPS。
决定系统运作的流程。例如,从Controller 接收到数据后要立刻转向(Redirect) 到另一个页面。
负责从Model 取得数据。我们可以在Controller 的类别中利用Model 提供的类别来取得数据。
决定应该显示哪个View 。一个网站里有很多呈现的View,要挑选哪一个View 来呈现给用户,是Controller 的责任。或是当Controller 运行的过程中发生异常时,也可由Controller 挑选适当的View 进行响应。
初探MVC 架构
看到这里,你可能还是一头雾水。没关系,接下来会开始探讨MVC中相当重要的架构观念。
M、V、C之间有很强的关联性与独立性,看似矛盾的解释,其中却有十分奥妙的分工与合作关系。
彼此的关联性
以“常规的”MVC解释法,彼此的关系如图1-2所示。
图 1-2 常规的MVC 示意图3
3实线表示主动角色,虚线表示被动角色。
Controller 与View 的关联性
在Controller 与View的关联性上,彼此都是双向关系,但Controller 的主动性较高。
当Controller 为“主动”角色时:
Controller 可决定要显示哪一个View 。
当View为“主动”角色时:
View 可决定数据要送回到哪一个Controller 的Action 。
当View 需要数据时,可决定数据应该从哪一个Controller 的Action 取得。
View 与Model 的关联性
在View与Model 的关联性上,View是站在“比较主动”的一方,而Model 则是以一个“数据服务提供商”的角度出发。
View的数据基本上是从Controller 传过来的,而传过来的数据型别却是Model 所定义的。因此,View与Model之间大多是“彼此参考”的关系,也就是View会参考Model 中的型别定义,如图1-3所示。
图 1-3 View 与Model 的关联性
若是发现从Controller 传到View的数据不足以完整显示,此时,View的角色就会立即转变为“主动”,直接对Model 进行数据查询,并取得数据。例如,通过ORM技术,可能会有延迟装入的机制,实际取得数据的时机,将是在View显示数据的时候。
除此之外,我们一般在规划网站时,通常会先规划界面的外观,借此定义该界面应该会出现什么数据或字段。例如,在网站企划文件中,出现的网页示意界面(Prototype) 如图1-4所示。
图 1-4 网页示意界面
像这个窗体有“公司账号”、“用户账号”、“密码”与“验证码”个字段,此时我们就会在Model 里,额外定义一个View所需要用到的数据模型(Model),这种专为View 所设计的Model ,我们称之为ViewModel 。
Model 与Controller 的关联性
在Controller 与Model 的关联性中,Controller 永远居于“主动”的角色。Controller 负责调用或使用Model 所定义的各种类别,而Model 很单纯的仅仅提供“数据服务”或“验证服务”给Controller 使用。
彼此的独立性
虽然M、V、C之间看似关联十分紧密,但是彼此间又不能拥有太强的相依性,否则就会失去我们采用MVC的初衷,因此M、V、C彼此之间的关系必须恰到好处。
Controller 与View 之间的独立性
从上一小节可以发现,Controller 跟View与Model 都有关联性,但神奇的是,彼此之间却维持着一种信任关系,称之为“常规(Convention) ”。
在大部分的Controller 定义中,通常不会明确指定要显示哪张View,而是由MVC Framework( 例如,ASP.NET MVC)依据常规帮你选出相关的View来显示,这就是Controller 与View之间的独立性。
换句话说,当View尚未被建构时,Controller 其实可以先撰写程序。
相对来说,View与Controller 的关系,大多是由Controller 主动去挑选适合的View来显示,所以,View本身对于“输出”的任务来说,是处于一个非常独立的状态。因此,当Controller 尚未被建构时,View也可以先撰写程序。
View 与Model 之间的独立性
View大多参考自Model 里所定义的型别,但这并非是绝对必要的。View不需要Model 也能够运作,只是这样View就会缺乏Model 所能提供的数据型别定义。这会造成View在开发时没有那么方便。
Model 与Controller 之间的独立性
Model 是贯穿全局的重要角色,有趣的是,在没有Model 的情况下,Controller 与View 一样可以运行得很好。例如,我们在显示“静态页面”时,没有任何动态数据的情况下,Controller 不需要从Model 取得数据,彼此之间当然就没什么关系了。
为什么要ASP.NET MVC
笔者从ASP.NET WebForm 转向到ASP.NET MVC 是个标准的典范转移(Paradigm Shift)案例,如果没有十足的好处,也不会贸然将全公司所有新的网站项目全部改用ASP.NET MVC,这其中的缘由将娓娓道来。
关注点分离与可维护性
在MVC的世界里,有个非常重要的观念就是“关注点分离(Separation of Concerns, SoC)”。意思是指当你在进行软件开发时,可以只关注在当前的对象上,不会受到相同系统中其他对象的干扰,包括所有对对象的修正也不会影响其他对象的运作,进而专注于完成工作,如此一来,不但容易提升软件质量,还可加快程序代码理解的速度。
MVC设计样式拥有清楚的开发架构与明确的对象分工,使软件更易于维护,若你是对面向对象编程颇有概念的人,就会了解好的对象架构带你上天堂,不好的对象架构带你住套房,老师说的话记得要听。
ASP.NET MVC拥有非常清楚的关注点分离架构,使各种不同大小的网站更容易维护,也能因应不同的需求来变更,以加速项目开发并提高更好的客户满意度。
开放特性与社群支持
ASP.NET MVC 从第一版正式版以来,就以微软公众授权(MS-PL) 开放源代码,也就是说,除了可以ASP.NET MVC 技术开发之外,还能自行更改、扩充ASP.NET MVC 的核心架构,甚至发行自己更改过的版本。即使到当前最新的ASP.NET MVC 4.0 也都维持一贯的授权方式,大家还可以到CodePlex 网站下载ASP.NET MVC 4.0 源代码回去研究。
重点提示 通过深入了解ASP.NET MVC 的运作原理以及源代码,进而会了解设计的原理与发生未知错误的原因。若要取得ASP.NET MVC 源代码请到以下网址下载:http://www.codeplex.com/aspnet
图 1-5 下载页面
虽然到目前为止,在全世界所有的ASP.NET开发人员里,ASP.NET WebForm 开发人员的人数还是大于ASP.NET MVC 开发人员的人数。不过这个比例正在逐渐缩小,近几年已经有越来越多的人体会到ASP.NET MVC 的好处,也陆续地加入了ASP.NET MVC的开发行列。
毕竟ASP.NET WebForm 框架的推出已经超过十年,对许多ASP.NET开发人员来说,ASP.NET MVC还是个新技术。不过,如果你经常逛国外网站的话,会发现活跃于ASP.NET MVC社群的开发人员,通常在技术层面都有一定的热情与程度,个个都是高手中的高手,笔者每次在国外发问,极少有无法解答的情况,而且每个回答既完整又专业。虽然网络上ASP.NET MVC 的相关文章还是以英文信息居多,但在国内也有越来越多的公司开始导入ASP.NET MVC 开发框架,因此也有不少热情的开发人员也会写出中文文章分享,另外在MSDN论坛上也有越来越多热情的技术同行也都会协助大家解决在ASP.NET MVC开发上的各种疑难杂症。以下列出几个跟ASP.NET MVC 相关的社群与讨论区,读者如果遇到任何问题,都可以到论坛上发问,只要问题描述得清楚,都会有高手跳出来帮你解决喔!
ASP.NET MVC 官方论坛
台湾微软MSDN 论坛 - ASP.NET 与AJAX(ASP.NET and AJAX) 讨论区
Stack Overflow - 所有ASP.NET MVC 问题
除此之外,当前也有许多由社群发展出来的ASP.NET MVC 函数库。例如,知名的 MVCContrib 就提供不少MVC相关的Helper 类别与各类函数库。以下列出一些不错的 ASP.NET MVC的相关函数库:
ASP.NET MVC / Web API / Web Pages
MVC Contrib 官方网站
MvcContrib Template Gallery
ASP.NET MVC SiteMap provider
ASP.net MVC Awesome - jQuery Ajax Helpers
Telerik Extensions for ASP.NET MVC
ASP.NET MVC Scaffolding
ASP.Net MVC Membership Starter Kit
XCaptcha - CAPTCHA Images for ASP.NET MVC
MvcExtensions
CommonLibrary.NET
开发工具与效率
正所谓“工欲善其事,必先利其器”,采用ASP.NET MVC 最大的优点就是可以通过Visual Studio 进行软件开发,尤其是Visual Studio 2012 新增许多ASP.NET MVC 的开发支持,绝对可以帮助你大幅提升开发效率。
在各家知名的MVC实作架构中,可以发现大多数会提供代码生成器工具,以协助快速构建MVC项目,当然ASP.NET MVC也不例外。除了通过强大的Visual Studio 开发工具来快速构建Model 对象(例如,LINQ to SQL 、Entity Framework 、Typed DataSet) 以外,还能利用Visual Studio 内建的T4工具与Scaffolding模板,快速创建Controller 与View 所需的代码。此外,通过自定义的T4代码生成器模板,也能快速地自动生成代码,进而达到快速开发之目的。
你可以试想一下,如果手上有一个500行的程序要写,是依据需求一行一行写出代码比较快?还是先通过代码生成器生成1 000 行代码,然后再依需求删减或是更改到500 行代码来得快?显而易见,当然是后者比较快,这是我们善用Visual Studio 开发工具的最主要的目的,也就是提升开发效率。
易于测试的架构
一般开发Web环境最困难之处在于测试,通常网站开发完成之后,会先由开发人员自行测试,包括确认页面链接以及窗体功能是否正确,待确认没问题之后再给客户测试,若客户确认没问题即可上线。
然而,在网站上线一阵子之后,总会有人想要变更需求,以及修正程序或者新增功能,这时就很容易会衍生出新的软件错误(Bug)。有时,开发人员会因为一些无心之过,而造成网站中的其他功能异常(例如,修正购物车的Bug却导致无法正常下单),在信息产业中可能时有耳闻,这种情况经常都必须等客户使用该功能时,才会发现有错误,等汇报给开发人员时,这些错误可能已经存在很久了。
ASP.NET MVC优先考虑“测试”的特性,让项目可通过各种测试框架(Test Framework) ,例如,Visual Studio 、Unit Test 、NUnit 等,轻易地实现测试导向开发流程(Test-Driven Development,TDD)到专案中。
易于分工的架构
由于关注点分离的特性,所以在项目开发的初期就能够进行分工,不用等到核心的函数库都完成后,才开始进行开发或集成。
仅这一点,就足以让我决心将所带领的软件开发团队,全部将技术转移到ASP.NET MVC开发框架,当前我所在的公司已经导入ASP.NET MVC 超过三年时间,所有人都打心底认同ASP.NET MVC 带来的价值,而我也坚信这样的投资与转变绝对是值得的。
总结
对于ASP/ASP.NET 开发人员来说,MVC或许是个新名词。微软长久以来致力于发展开发工具,希望能降低开发上的负担。但在发展的过程中,却造成开发人员过度依赖开发工具,反而降低了对于网络原理、面向对象、设计样式等基础知识的学习意愿,这并不是好的迹象。
因此,我鼓励开发人员能多接触各种不同的技术领域,甚至学习不只一种程序语言,除了能开阔视野外,也能够激发不少开发上的创意与乐趣。
M、V、C 之间必须有点黏又不能太黏,这一切会取决于“你”,但基于MVC Pattern 的精神,建议应该适度调整你的MVC 架构,最终目标就是开发出一套架构清晰,又易于维护的软件,永远不要忘记你采用MVC Pattern 的初衷。