ASP.NET 中的设计模式之MVC篇
- 设计模式
- MVC
- 页面控制器
- 模板与Page基类
设计模式
在这里,笔者无意对设计模式的含义进行过多介绍或者严格定义,只是给一个比较简单的理解:设计模式是对一些经常出现问题的一种解决方式,这种解决方式来自于许多开发人员的经验总结。
MVC—WEB开发中最基本的设计模式
在经典的设计模式书籍中,常用的设计模式有二三十种。就
WEB
应用程序开发来说,
MVC
可能是最基本的一种设计模式了。的确,
WEB
应用程序有一些特有的问题,瘦客户端
、不连续的状态、对友善界面的关注,以及愈来愈多的终端设备
…
。
MVC
将程序功能分成三部分考虑:视图
(VIEW
,即可见的用户界面部分
)
、模型(
Model
,程序的数据模型和业务逻辑部分),控制器(
Controller
,根据用户输入通知模型和视图进行相应更改)。
MVC
将
WEB
应用程序的一个页面分成若干部分,当对其中的一部分修改时,另外一部分可能只需要很少的变动甚至保持原样,使得应用程序对需求变化的适应性更好。
此外,
WEB
应用程序往往既要实现美观的用户界面,又要实现精确复杂的商务逻辑,然而并不是所有的人都能同时做好这两件事情。有了
MVC
,开发团队成员间的协作就比较容易了。
ASP.NET中的MVC
ASP.NET
对
MVC
提供了支持。编写代码隐藏的网页时,程序代码被分为两部分:
.ASPX
页面文件和一个单独的类文件
(.CS
文件
)
。
在图
1
所示的例子中,用户从下拉框选择图书类别,提交后,列出该类别下的书籍清单。
图 1 示例
视图
此例子的
View
部分在
.ASPX
文件中:
<%
@ Page language
=
"
c#
"
Codebehind
=
"
WebForm1.aspx.cs
"
AutoEventWireup
=
"
false
"
Inherits
=
"
TempSite.WebForm1
"
%>
< HTML >
< HEAD >< title > BookView </ title ></ HEAD >
< body >
< form id = " Form1 " method = " post " runat = " server " >
< FONT face = " 宋体 " >< h4 > 图书浏览 </ h4 >
< asp:Label id = " Label1 " style = " TOP: 56px "
runat = " server " Width = " 80px " Height = " 24px " > 选择类别: </ asp:Label >
</ FONT >
< asp:DropDownList id = " bookClassSelect " style = " TOP: 56px " runat = " server " Width = " 192px " Height = " 24px " ></ asp:DropDownList >
< asp:Button id = " Button1 " style = " TOP: 56px " runat = " server "
Width = " 72px " Text = " 确定 " ></ asp:Button >
< br >& nbsp; < br >
< asp:DataGrid id = " bookDataGrid1 "
HeaderStyle - BackColor = " #aaaadd " BackColor = " #ccccff "
style = " font-size:8pt; "
runat = " server " Width = " 75% " CellPadding = 3 CellSpacing = 0
BorderColor = " #000000 " ></ asp:DataGrid >
</ form >
</ body >
</ HTML >
< HTML >
< HEAD >< title > BookView </ title ></ HEAD >
< body >
< form id = " Form1 " method = " post " runat = " server " >
< FONT face = " 宋体 " >< h4 > 图书浏览 </ h4 >
< asp:Label id = " Label1 " style = " TOP: 56px "
runat = " server " Width = " 80px " Height = " 24px " > 选择类别: </ asp:Label >
</ FONT >
< asp:DropDownList id = " bookClassSelect " style = " TOP: 56px " runat = " server " Width = " 192px " Height = " 24px " ></ asp:DropDownList >
< asp:Button id = " Button1 " style = " TOP: 56px " runat = " server "
Width = " 72px " Text = " 确定 " ></ asp:Button >
< br >& nbsp; < br >
< asp:DataGrid id = " bookDataGrid1 "
HeaderStyle - BackColor = " #aaaadd " BackColor = " #ccccff "
style = " font-size:8pt; "
runat = " server " Width = " 75% " CellPadding = 3 CellSpacing = 0
BorderColor = " #000000 " ></ asp:DataGrid >
</ form >
</ body >
</ HTML >
例子程序中的
Model
和
Controller
部分则在独立的类文件中:
using
System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
namespace TempSite {
public class WebForm1 : System.Web.UI.Page {
protected System.Web.UI.WebControls.DropDownList bookClassSelect;
protected System.Web.UI.WebControls.Button Button1;
protected System.Web.UI.WebControls.DataGrid bookDataGrid1;
protected System.Web.UI.WebControls.Label Label1;
private void Page_Load(object sender, System.EventArgs e)
{
if(!Page.IsPostBack){
string strcmd="Select * From BookClass";
SqlConnection sqlCn=new SqlConnection(
"server=localhost;database=guanxi;uid=sa;pwd=pass");
SqlDataAdapter sqlCommand=new
SqlDataAdapter(strcmd,sqlCn);
DataSet ds=new DataSet();
sqlCommand.Fill(ds,"BookClass");
bookClassSelect.DataSource=ds;
bookClassSelect.DataTextField="ClassName";
bookClassSelect.DataValueField="ClassID";
bookClassSelect.DataBind();}
}
Web 窗体设计器生成的代码#region Web 窗体设计器生成的代码
override protected void OnInit(EventArgs e){
InitializeComponent();
base.OnInit(e); }
private void InitializeComponent(){
this.Button1.Click = new
System.EventHandler(this.Button1_Click);
this.Load = new System.EventHandler(this.Page_Load);}
#endregion
private void Button1_Click(object sender, System.EventArgs e){
string cmd=string.Format("SELECT BookID,BookName,[Count]
FROM BookInfo Where BookClassID={0}"
,this.bookClassSelect.SelectedItem.Value);
SqlConnection sqlCn=new SqlConnection(
"server=localhost;database=guanxi;uid=sa;pwd=pass");
SqlDataAdapter sqlCommand=new SqlDataAdapter(cmd,sqlCn);
DataSet ds=new DataSet();
sqlCommand.Fill(ds,"Book");
bookDataGrid1.DataSource=ds;
bookDataGrid1.DataBind();}
}
}
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
namespace TempSite {
public class WebForm1 : System.Web.UI.Page {
protected System.Web.UI.WebControls.DropDownList bookClassSelect;
protected System.Web.UI.WebControls.Button Button1;
protected System.Web.UI.WebControls.DataGrid bookDataGrid1;
protected System.Web.UI.WebControls.Label Label1;
private void Page_Load(object sender, System.EventArgs e)
{
if(!Page.IsPostBack){
string strcmd="Select * From BookClass";
SqlConnection sqlCn=new SqlConnection(
"server=localhost;database=guanxi;uid=sa;pwd=pass");
SqlDataAdapter sqlCommand=new
SqlDataAdapter(strcmd,sqlCn);
DataSet ds=new DataSet();
sqlCommand.Fill(ds,"BookClass");
bookClassSelect.DataSource=ds;
bookClassSelect.DataTextField="ClassName";
bookClassSelect.DataValueField="ClassID";
bookClassSelect.DataBind();}
}
Web 窗体设计器生成的代码#region Web 窗体设计器生成的代码
override protected void OnInit(EventArgs e){
InitializeComponent();
base.OnInit(e); }
private void InitializeComponent(){
this.Button1.Click = new
System.EventHandler(this.Button1_Click);
this.Load = new System.EventHandler(this.Page_Load);}
#endregion
private void Button1_Click(object sender, System.EventArgs e){
string cmd=string.Format("SELECT BookID,BookName,[Count]
FROM BookInfo Where BookClassID={0}"
,this.bookClassSelect.SelectedItem.Value);
SqlConnection sqlCn=new SqlConnection(
"server=localhost;database=guanxi;uid=sa;pwd=pass");
SqlDataAdapter sqlCommand=new SqlDataAdapter(cmd,sqlCn);
DataSet ds=new DataSet();
sqlCommand.Fill(ds,"Book");
bookDataGrid1.DataSource=ds;
bookDataGrid1.DataBind();}
}
}
分离模型和控制器
代码隐藏文件自动实现的
MVC
模式中,只是实现了
View
和
Model-Controller
的分离,还没有解决代码复用问题。
对此的解决方式是,进一步将模型和控制器分离。
将模型分离到单独的类文件中,使其只包含与数据库、业务逻辑相关的代码,其它的页面可很容易使用此代码:
尽管页面中的公共方法也可以在其它页面调用,但不推荐该方式,因为会导致页面间的藕合度增加。
控制器部分的代码仍然在代码隐藏文件中,但其逻辑已经非常清晰:
private
void
Page_Load(
object
sender, System.EventArgs e)
{
if(!Page.IsPostBack){
DataSet ds=DataBaseGateWay.GetBookClass();
bookClassSelect.DataSource=ds;
bookClassSelect.DataTextField="ClassName";
bookClassSelect.DataValueField="ClassID";
bookClassSelect.DataBind();}
}
override protected void OnInit(EventArgs e) {
InitializeComponent();
base.OnInit(e); }
private void InitializeComponent() {
this.Button1.Click = new
System.EventHandler(this.Button1_Click);
this.Load = new System.EventHandler(this.Page_Load);}
private void Button1_Click( object sender, System.EventArgs e) {
DataSet ds=new DataSet();
sqlCommand.Fill(ds,"Book");
bookDataGrid1.DataSource=ds;
bookDataGrid1.DataBind();}
}
if(!Page.IsPostBack){
DataSet ds=DataBaseGateWay.GetBookClass();
bookClassSelect.DataSource=ds;
bookClassSelect.DataTextField="ClassName";
bookClassSelect.DataValueField="ClassID";
bookClassSelect.DataBind();}
}
override protected void OnInit(EventArgs e) {
InitializeComponent();
base.OnInit(e); }
private void InitializeComponent() {
this.Button1.Click = new
System.EventHandler(this.Button1_Click);
this.Load = new System.EventHandler(this.Page_Load);}
private void Button1_Click( object sender, System.EventArgs e) {
DataSet ds=new DataSet();
sqlCommand.Fill(ds,"Book");
bookDataGrid1.DataSource=ds;
bookDataGrid1.DataBind();}
}
对控制器和视图的进一步重构
B/S
结构的应用程序被分成了一个一个的页面。开发过大一些应用程序的人可能都知道,在商业应用程序的不同页面中,常常有许多相同的页面元素需要保持一致,如
Banner
、菜单、页脚等。此外,在不同的页面上,也有许多非常相似的事情需要处理,它们可能包括:对用户身份和权限的验证、接收用户传递的请求、对错误的处理,甚至可以包括更多(例如对缓存的处理)。显然,这可以为多个页面共享的二者分属于
MVC
中的不同范畴:界面属于
View
,而事务的处理则属于控制器。
在前面为了实现复用,我们曾经将模型分离到了单独的类文件中实现,而对于可能为多个页面共享的视图和控制器,如果仍然为每一个页面都单独编写一个复杂而臃肿的视图与控制器、或者在页面间复制拷贝、或者互相调用不同页面的成员,也都不是好的选择。
模板技术
在
Web
开发中,模板技术常常是人们用来解决视图重用的一种较好选择。其基本思想是为多个页面定义统一的外观和布局,然后在每个单独的页面加载过程中,用该页面内容替换或者嵌入到模板内容中。
ASP.NET
中,也可以使用模板方式。例如,图
2
所示的页面中,上面和左边的部分都是站点多个页面的公共部分,只有页面中间的部分是每一个页面单独维护的内容。对此,可以采用模板方式,只在一个称为
Template
的视图文件中编写公共的外观,而不需要在每一个页面都重新把这些代码复制一遍。
图 2 使用模板实现页面的一个简单例子
实现这个例子的模板代码如下:图 2 使用模板实现页面的一个简单例子
<%
@ Control Language
=
"
c#
"
AutoEventWireup
=
"
false
"
Codebehind
=
"
Template1.ascx.cs
"
Inherits
=
"
M161.CS.Web.Award.Template1
"
TargetSchema
=
"
http://schemas.microsoft.com/intellisense/ie5
"
%>
<% @ Register TagName = " AwardHeader " TagPrefix = " CC " Src = " AwardHeader.ascx " %>
<% @ Register TagName = " AwardNav " TagPrefix = " CC " Src = " AwardNav.ascx " %>
< HTML >< HEAD >
< LINK href = " ../default.css " type = " text/css " rel = " stylesheet " >
</ HEAD >
< body onload = " onload(); " >
< form id = " Form1 " method = " post " runat = " server " >
< div >< CC:AwardHeader runat = " server " ID = " Header1 " /></ div >
< div style = " WIDTH:100% " >
< table width = " 100% " border = " 0 " cellpadding = " 0 " cellspacing = " 0 " id = " TableTemplate " >
< tr >
< td style = " width:150px " >
< CC:AwardNav runat = " server " ID = " Awardnav1 " />
</ td >
< td valign = " top " align = left >
< asp:PlaceHolder id = " _placeHolder " runat = " server "
EnableViewState = " False " />
</ td >
</ tr >
</ table >
</ div >
</ form >
</ body >
</ HTML >
<% @ Register TagName = " AwardHeader " TagPrefix = " CC " Src = " AwardHeader.ascx " %>
<% @ Register TagName = " AwardNav " TagPrefix = " CC " Src = " AwardNav.ascx " %>
< HTML >< HEAD >
< LINK href = " ../default.css " type = " text/css " rel = " stylesheet " >
</ HEAD >
< body onload = " onload(); " >
< form id = " Form1 " method = " post " runat = " server " >
< div >< CC:AwardHeader runat = " server " ID = " Header1 " /></ div >
< div style = " WIDTH:100% " >
< table width = " 100% " border = " 0 " cellpadding = " 0 " cellspacing = " 0 " id = " TableTemplate " >
< tr >
< td style = " width:150px " >
< CC:AwardNav runat = " server " ID = " Awardnav1 " />
</ td >
< td valign = " top " align = left >
< asp:PlaceHolder id = " _placeHolder " runat = " server "
EnableViewState = " False " />
</ td >
</ tr >
</ table >
</ div >
</ form >
</ body >
</ HTML >
using
System;
using System.Data;
using System.Data.SqlClient;
public class DataBaseGateWay {
public static DataSet GetBookClass(){
//
}
public static DataSet GetBookList(string BookClassID){
//
}
}
using System.Data;
using System.Data.SqlClient;
public class DataBaseGateWay {
public static DataSet GetBookClass(){
//
}
public static DataSet GetBookList(string BookClassID){
//
}
}