Page Controller的实现需要在基类中为页面的公共部分创建代码,但是随着时间的推移,需求会发生较大的改变,有时不得不增加非公用的代码,这样基类就会不断增大,您可能会创建更深的继承层次结构以删除条件逻辑,这样一来我们很难对它进行重构,因此需要更进一步对Page Controller进行研究。
Front Controller通过对所有请求的控制并传输解决了在Page Controller中存在的分散化处理的问题,它分为Handler和Command树两个部分,Handler处理所有公共的逻辑,接收HTTP Post或Get请求以及相关的参数并根据输入的参数选择正确的命令对象,然后将控制权传递到Command对象,由其完成后面的操作,在这里我们将使用到Command模式。
Command模式通过将请求本身变成一个对象可向未指定的应用对象提出请求,这个对象可被存储并像其他的对象一样被传递,此模式的关键是一个抽象的Command类,它定义了一个执行操作的接口,最简单的形式是一个抽象的Execute操作,具体的Command子类将接收者作为其一个实例变量,并实现Execute操作,指定接收者采取的动作,而接收者具有执行该请求所需的具体信息。
关于Handler的原理请查阅MSDN,在这就不多讲了,我们来看看Front Controller模式的具体实现:
首先在 Web.Config里定义:
〈!-- 指定对Dummy开头的aspx文件交由Handler处理 -->
〈httpHandlers>
〈add verb="*" path="/WebPatterns/FrontController/Dummy*.aspx" type="WebPatterns.FrontController.Handler,WebPatterns"/>
〈/httpHandlers>
〈!-- 指定名为FrontControllerMap的页面映射块,交由UrlMap类处理,程序将根据key找到对应的url作为最终的执行路径,您在这可以定义多个key与url的键值对 -->
〈configSections>
〈section name="FrontControllerMap" type="WebPatterns.FrontController.UrlMap, WebPatterns">〈/section>
〈/configSections>
〈FrontControllerMap>
〈entries>
〈entry key="/WebPatterns/FrontController/DummyWebForm.aspx" url="/WebPatterns/FrontController/ActWebForm.aspx" />
。。。
〈/entries>
〈/FrontControllerMap>
修改webForm.aspx.cs:
private void button_Click( object sender, System.EventArgs e )
{
Response.Redirect( "DummyWebForm.aspx?requestParm=" + dropDownList.SelectedValue );
}
当程序执行到这里时将会根据Web.Config里的定义触发类Handler的ProcessRequest事件:
Handler.cs:
public class Handler : IHttpHandler
{
public void ProcessRequest( HttpContext context )
{
Command command = CommandFactory.Make( context.Request.Params );
command.Execute( context );
}
public bool IsReusable
{
get
{
return true;
}
}
}
而它又会调用类CommandFactory的Make方法来处理接收到的参数并返回一个Command对象,紧接着它又会调用该Command对象的 Execute方法把处理后参数提交到具体处理的页面。
public class CommandFactory
{
public static Command Make( NameValueCollection parms )
{
string requestParm = parms["requestParm"];
Command command = null;
//根据输入参数得到不同的Command对象
switch ( requestParm )
{
case "1" :
command = new FirstPortal();
break;
case "2" :
command = new SecondPortal();
break;
default :
command = new FirstPortal();
break;
}
return command;
}
}
public interface Command
{
void Execute( HttpContext context );
}
public abstract class RedirectCommand : Command
{
//获得Web.Config中定义的key和url键值对,UrlMap类详见下载包中的代码
private UrlMap map = UrlMap.SoleInstance;
protected abstract void OnExecute( HttpContext context );
public void Execute( HttpContext context )
{
OnExecute( context );
//根据key和url键值对提交到具体处理的页面
string url = String.Format( "{0}?{1}", map.Map[ context.Request.Url.AbsolutePath ], context.Request.Url.Query );
context.Server.Transfer( url );
}
}
public class FirstPortal : RedirectCommand
{
protected override void OnExecute( HttpContext context )
{
//在输入参数中加入项portalId以便页面处理
context.Items["portalId"] = "1";
}
}
public class SecondPortal : RedirectCommand
{
protected override void OnExecute(HttpContext context)
{
context.Items["portalId"] = "2";
}
}
最后在ActWebForm.aspx.cs中:
dataGrid.DataSource = GetSubjectDataSource( HttpContext.Current.Items["portalId"].ToString() );
dataGrid.DataBind();
上面的例子展示了如何通过Front Controller集中和处理所有的请求,它使用CommandFactory来确定要执行的具体操作,无论执行什么方法和对象,Handler只调用 Command对象的Execute方法,您可以在不修改 Handler的情况下添加额外的命令。它允许让用户看不到实际的页面,当用户输入一个URL时,然后系统将根据web.config文件将它映射到特定的URL,这可以让程序员有更大的灵活性,还可以获得Page Controller实现中所没有的一个间接操作层。
对于相当复杂的Web应用我们才会采用Front Controller模式,它通常需要将页面内置的Controller替换为自定义的Handler,在Front Controllrer模式下我们甚至可以不需要页面,不过由于它本身实现比较复杂,可能会给业务逻辑的实现带来一些困扰。
以上两个 Controller模式都是处理比较复杂的WebForm应用,相对于直接处理用户输入的应用来讲复杂度大大提高,性能也必然有所降低,为此我们最后来看一个可以大幅度提高程序性能的模式:Page Cache模式。
Page Cache模式下的WebForm
几乎所有的WebForm面临的都是访问很频繁,改动却很少的应用,对WebForm的访问者来说有相当多的内容是重复的,因此我们可以试着把 WebForm或者某些相同的内容保存在服务器内存中一段时间以加快程序的响应速度。
这个模式实现起来很简单,只需在页面上加入:
〈%@ OutputCache Duration="60" VaryByParam="none" %>,
这表示该页面会在60秒以后过期,也就是说在这60秒以内所有的来访者看到该页面的内容都是一样的,但是响应速度大大提高,就象静态的HTML页面一样。
也许您只是想保存部分的内容而不是想保存整个页面,那么我们回到MVC模式中的SQLHelper.cs,我对它进行了少许修改:
public static DataSet GetPortal()
{
DataSet dataSet;
if ( HttpContext.Current.Cache["SELECT_PORTAL_CACHE"] != null )
{
//如果数据存在于缓存中则直接取出
dataSet = ( DataSet ) HttpContext.Current.Cache["SELECT_PORTAL_CACHE"];
}
else
{
// 否则从数据库中取出并插入到缓存中,设定绝对过期时间为3分钟
dataSet = GetDataSet( SQL_SELECT_PORTAL );
HttpContext.Current.Cache.Insert( "SELECT_PORTAL_CACHE", dataSet, null, DateTime.Now.AddMinutes( 3 ), TimeSpan.Zero );
}
return dataSet;
}
在这里把SELECT_PORTAL_CACHE作为Cache的键,把GetDataSet( SQL_SELECT_PORTAL )取出的内容作为Cache的值。这样除了程序第1次调用时会进行数据库操作外,在Cache过期时间内都不会进行数据库操作,同样大大提高了程序的响应能力。
小结
自从.NET框架引入设计模式以后在很大程度上提高了其在企业级应用方面的实力,可以毫不夸张的说在企业级应用方面.NET已经赶上了Java的步伐并大有后来居上之势,本文通过一个实例的讲解向读者展示了在.NET框架下实现Web设计模式所需的一些基本知识。