ASP.NET 中的设计模式之MVC篇

ASP.NET 中的设计模式之MVC篇

 

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 所示的例子中,用户从下拉框选择图书类别,提交后,列出该类别下的书籍清单。

 


示例

视图

此例子的 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 >

 

例子程序中的 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();}

    }

    
#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 的分离,还没有解决代码复用问题。 对此的解决方式是,进一步将模型和控制器分离。

将模型分离到单独的类文件中,使其只包含与数据库、业务逻辑相关的代码,其它的页面可很容易使用此代码:

using  System;
using  System.Data;
using  System.Data.SqlClient;
public   class  DataBaseGateWay {
    
public   static  DataSet GetBookClass() {
        
//
    }

    
public   static  DataSet GetBookList( string  BookClassID) {
            
//
    }

}
 

尽管页面中的公共方法也可以在其它页面调用,但不推荐该方式,因为会导致页面间的藕合度增加。

 

控制器部分的代码仍然在代码隐藏文件中,但其逻辑已经非常清晰:

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();}

}

 

 

对控制器和视图的进一步重构

B/S 结构的应用程序被分成了一个一个的页面。开发过大一些应用程序的人可能都知道,在商业应用程序的不同页面中,常常有许多相同的页面元素需要保持一致,如 Banner 、菜单、页脚等。此外,在不同的页面上,也有许多非常相似的事情需要处理,它们可能包括:对用户身份和权限的验证、接收用户传递的请求、对错误的处理,甚至可以包括更多(例如对缓存的处理)。显然,这可以为多个页面共享的二者分属于 MVC 中的不同范畴:界面属于 View ,而事务的处理则属于控制器。

 

在前面为了实现复用,我们曾经将模型分离到了单独的类文件中实现,而对于可能为多个页面共享的视图和控制器,如果仍然为每一个页面都单独编写一个复杂而臃肿的视图与控制器、或者在页面间复制拷贝、或者互相调用不同页面的成员,也都不是好的选择。

模板技术

Web 开发中,模板技术常常是人们用来解决视图重用的一种较好选择。其基本思想是为多个页面定义统一的外观和布局,然后在每个单独的页面加载过程中,用该页面内容替换或者嵌入到模板内容中。

 

ASP.NET 中,也可以使用模板方式。例如,图 2 所示的页面中,上面和左边的部分都是站点多个页面的公共部分,只有页面中间的部分是每一个页面单独维护的内容。对此,可以采用模板方式,只在一个称为 Template 的视图文件中编写公共的外观,而不需要在每一个页面都重新把这些代码复制一遍。  

  
    使用模板实现页面的一个简单例子

实现这个例子的模板代码如下:

<% @ 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 = " javascript: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 >

 

在这部分代码中,是将页面公共部分做成了控件,然后在模板中注册。尽管在每一个页面中注册一遍控件所需代码也不是很多,但通过模板维护公共部分有一个好处:可以更快速地改变整个应用程序的外观、布局、颜色等,也可以准备多个不同的模板,并为不同的页面选择其合适的模板。

Page 基类

要采用模板来实现页面,除了要将模板的内容包含起来,往往还要在页面中替换或者填充模板中的一些内容。这些装载、转换工作既然也是每一个页面都要做的事情,当然也可以将它们放到一个单独的类中实现,这就是 Page 基类。

Page 基类在 ASP.NET 中的角色是公共的控制器。我们可以将每一个页面都要实现的控制逻辑放到此处。

下面的代码文件是实现图 2 Page 基类。
using  System;
using  System.Collections;
using  System.Web;
using  System.Web.UI;
using  System.Web.UI.WebControls;
using  System.Web.UI.HtmlControls;

namespace  MyNameSpace
{
    
///   <summary>
    
/// 系统各页面的BasePage
    
///  作用:通过装载模板为各个页面加载公共布局,包括Header、导航条、页脚
    
///   </summary>

     public   class  MyBasePage:Page
    
{
        PlaceHolder _container;
        
string       _templatePath  =   " Template1.ascx " ;

        
public  MyBasePage()
        
{
            _container 
=   new  PlaceHolder();
            _container.ID 
=   " _container " ;
        }

        
        
protected   string  TemplatePath
        
{     // 允许指定不同的模板文件
             get   return  _templatePath;  }
            
set   { _templatePath  =  value; }
        }


        
protected   override   void  OnInit(EventArgs e)
        
{
            
// 装载模板文件
            Control template  =  LoadControl(_templatePath);
            
if  (template  ==   null )
                
throw   new  Exception( " 装载模板文件错误! " );
            Control placeHolder 
=  
                template.FindControl(
" _placeHolder " );
            
if  (placeHolder  ==   null )
                
throw   new  Exception( " 模板文件格式错误 " );
            placeHolder.Controls.Add(_container);
            
for  ( int  i = 0 ; i < template.Controls.Count; i ++ )
            
{
                Control c 
=  template.Controls[ 0 ];
                template.Controls.Remove(c);
                Controls.Add(c);
            }

              
base .OnInit(e);
        }

    }

}

  其它页面继承 Page 基类,而不是继承默认的 System.Web.UI.Page 类。
   // page1.aspx.cs
public   class  page1 : MyBasePage
{
    
//
  }


<! —page1.aspx
<% @ Page Inherits = " page1 "  CodeBehind = " page1.aspx.cs "  Language = " c# "  AutoEventWireup = " false "   %>
< h5 > This  is  page1 </ h5 >

 

公共的 Page 基类可以做很多事情,常见的如对错误 (Exception) 的处理、接收页面传递的字段、 URL 与字符集转换、增加客户端脚本等。

在性能方面,使用 ACT 测试的结果表明,使用 Page 基类和模板技术带来的性能损失非常有限。

最后,顺便提到一下笔者认为非常有意思的一点:在 ASP.NET 中,无论是模板、 Page 基类、还是独立的页面,都可以同时具有视图文件 (.aspx 文件 ) 和代码文件 (.cs 文件 ) 其中之一或者全部。例如,在微软资助出版的 ESP (即 .NET 企业解决方案模式)书籍中,就提供了另外一种 Page 基类,该 page 基类的视图文件就是各个页面的公共视图,也相当于我们这里的模板文件。 ASP.NET 中这种灵活的机制既为大多数开发人员提供了方便,也同时增加了犯错的可能 --- 尽管你可以用多种方式实现同样的功能,但最佳的方式是什么呢?正如我们已经找到了复用 MVC 三者中任何一种的方式,但对于 MVC 究竟是分离还是合并到一起,仍然需要根据应用程序的复杂程度等实际情况做出选择。

-------------------------------------------------------------------------------------------

参考文献:
1.微软.NET企业解决方案模式
 http://www.microsoft.com/china/MSDN/library/architecture/patterns/esp/espdefault.mspx

2.CodeProject文章
http://www.codeproject.com/aspnet/page_templates.asp#xx1044454xx

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值