使用模板在php和java里很普及。微软官方没有给我门提供类似php模板的工具类。应为aspx页面本身就是模板
。控件也是模板,并且微软官方提供的接口之丰富,功能支持之多使他们没有必要在提供类似php模板之类
的工具类库。
标准的aspx页面也是可以实现MVC的。但是有的时候会觉得MVC功能实现的不是很彻底。比如一个程序员和一个美工结合。可以对页面进行逻辑和外观上的改版。但是一个美工自己是只能修改外观布局。逻辑功能暴露给美工的控制接口太少了。经管有控件的属性。还有<% = Value %> 这样的语法可以暴露在aspx页面上。但是有多少美工熟悉aspx各个控件的属性那。<% = Value %> 这样的语法只是输出结果,对内部逻辑是没办法控制的。即使用<% =函数(参数) %> 这样的语句,修改参数。也不是完善的解决方法。 所以模板类第一个需求是让MVC彻底一些。把逻辑功能更多的开放给美工。
WEBFORM框架设计的很优秀,里面功能很多,结构复杂。如果我对一种特定的web请求,只抽出WEBFORM框架内我需要的一部分功能。而舍弃对这次请求无用的功能。那么服务器对web请求处理的速度肯定会大幅度提高。这样模板类第二个需求是想尽一切办法提高对web请求的响应速度。
结合上面两点,自己设计了一个轻量级的web开发架构。让页面都通过模板来生成。并设计了对应此种模板的适合的使用方法。类似功能的代码在google上一搜一堆。但使用自己开发的web开发架构,用起来很习惯。开发速度快上了不少。一个人用这种架构,很轻松的完成了一个原先看来需要几个人在相同时间才可以完成的项目。现在写了一个完整的使用demo,里面有我习惯的使用方法。可以让大家更好的了解使用方法。同时也是我门公司新员工的培训demo。下载地址 http://files.cnblogs.com/vitality2007/WebSite_Demo_Down.rar
我门先从速度出发,aspx的低层有很多文章在谈。对来访的web请求最先处理的是http处理模块,然后是http处理程序。想要做快速响应,就是放弃WEBFORM框架不必要的部分。优化处理必要的功能。以前很多文章都介绍了一个方法,就是自己实现http处理程序。我门现在也使用这个方法。自己实现http处理程序。当并发很大的时候,异步页面是一个很好的解决方法。于是就象url重写一样。我门让一些对aspx的请求。直接转到模板类的异步http处理程序上。在通过自己的处理类来实现一个页面的请求处理。
在单内核单cpu的机器上对web请求使用异步处理模式决不是一个好注意。现在的模板类是要在特定的cpu上,多内核或多cpu才可以发挥最大性能的。不过对web服务器来说,这个要求一般是能达到的。通过CLR的线程池来执行异步处理。尽可能减少创建线程和销毁线程的开销。
这里在说一点.对http处理器类只要继承AsynchRunTimeTemplet类就可以获得异步处理功能,但毕竟创建一个线程是要消耗一定cpu,内存资源的。所以只要让http处理器类继承RunTimeTemplet类,就可以获得同步处理功能。在同步和异步之间的选择是很难有定论。当页面处理逻辑小,站点并发底的时候。应该使用同步处理程序。当站点并发高,页面逻辑处理多的时候。应该选用异步处理程序。所以决定某个模板处理类是同步执行还是异步执行需要你综合自己服务器的cpu,内存,带宽,web请求并发数量。页面处理逻辑大小.。做动态改变。
对http处理器类提供两个基类 AsynchRunTimeTemplet 和 RunTimeTemplet ,分别提供 异步 和同步 处理支持。是有点麻烦的,不如合成一个可以同时支持同步和异步的基类。这样做可以实现,但是在每一个web请求到来的时候,都要经理很多处理,判断。后来为了速度,我就去掉这个功能,就提供对同步处理和异步处理两个类。当然可下载的demo内所有的处理都是异步处理的。
有的时候直接在代码里获得一个模板文件的解析结果,在通过<% = Value %> 或<asp;label />某种方式在页面上显示是很有必要的。所以这个http处理程序内部要有一个切入点。可以通过函数调用的方式来模拟一个web请求的来访。直接返回模板页面的执行结果。但这个过程是同步来执行的。所以在代码级别提供两个函数
//直接获取 /Templet/List.htm 模板文件的解析结果
ShowHtml = TempletHandler.GetFileString("/Templet/List.htm", TempletHandler.GetRunTime());
//这个函数的意义是 获取/Templet/List.htm模板的解析结果 写到/CreatPage/List.shtml 文件内 , 同步执行
TempletHandler.WriteFile("/Templet/List.htm", "/CreatPage/List.shtml", TempletHandler.GetRunTime());
这样在传统的aspx页内使用模板类,或者生成静态页的需求都完美的解决了。
在模板级提供两个系统标签
{$Include:/Templet/Bottom.htm::缓存$} 直接包含一个静态页面,
{$ExecInclude:/Templet/ExecInclude.htm$} 直接包含一个模板页的解析结果。实现aspx可以包含动态页的效果。但我想这实用的意义可能不大。
这两个标签名是固定名字,直接用就可以,不需要写自己定义解析函数了。到这我就得说一下,模板架构是建设好了,但是提供的系统函数真是少。现在才有两个 Include 和 ExecInclude 。并且我没有在模板页上加上一些变量定义,bool判断的功能。还把以前的loop循环功能也去掉了。应为我觉得,使用这个类库。所有的显示。都可以在对应的标签解析函数内解决。开发设计他是为程序服务的。不是给美工,编辑服务的。是为了提供更好的性能。快速开发。当然,编辑美工可以修改标签的参数,作到控制。
demo包含5个项目,首先看LogicLayer 逻辑层 我定义了两个类, TempletHandler 类用来实现http处理器,SearchTemplet类用来为来访web请求和各个模板处理类做匹配。先看实现http处理器的TempletHandler 类,这个类本身没有多少具体代码,只要继承AsynchRunTimeTemplet 类。和实现IHttpHandlerFactory, IRequiresSessionState接口就可以。
http处理器类
#region http处理器类
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// http处理器类
/// </summary>
public sealed class TempletHandler : AsynchRunTimeTemplet, IHttpHandlerFactory, IRequiresSessionState
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
核心函数#region 核心函数
public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
return AsynchRunTimeTemplet.HandlerFactory(url, this);
}
#endregion
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
为实现接口写上#region 为实现接口写上
public void ReleaseHandler(IHttpHandler handler)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
}
#endregion
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
类文件和要显示页面匹配的函数#region 类文件和要显示页面匹配的函数
protected override AsynchBaseTempletHandler AddTempletClass(string FilePath)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
AsynchBaseTempletHandler BackObject = null;
SearchTemplet.SearchTempletUrl(ref BackObject, FilePath);
return BackObject;
}
#endregion
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
获得实例属性#region 获得实例属性
private readonly static AsynchRunTimeTemplet _RunTimeTemplet = new TempletHandler();
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public static AsynchRunTimeTemplet GetRunTime()
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
return _RunTimeTemplet;
}
#endregion
}
#endregion
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
有了这个类以后,下面就可以在配置文件内加上
<
httpHandlers
>
<
add
verb
="*"
path
="/WebSiteDemo/Page/*.aspx"
type
="LogicLayer.TempletHandler,LogicLayer"
/>
</
httpHandlers
>
说明在我门的demo里,所有对page目录下的请求,都转为模板类来处理。在看一下查找模板类 SearchTemplet 。
查找模板类
#region 查找模板类
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// 查找模板类
/// </summary>
public static class SearchTemplet
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
public static void SearchTempletUrl(ref AsynchBaseTempletHandler BackObject, string FilePath)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
switch (FilePath)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
//这个是模板文件的路径,但是却永远不会直接访问到,他是给同步执行来函数来调用的
case @"/Templet/List.htm":
BackObject = new ArticleTempletList();
break;
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
case @"/Page/Article.aspx": //说明这个请求由什么处理类来处理
BackObject = new ShowArticleTemplet();
break;
default:
throw new Exception("没有匹配项目,自己修改为合适的表示形式");
break;
}
}
}
#endregion
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
文章列表页是List.aspx 我门看看他的代码
protected
void
Page_Load(
object
sender, EventArgs e)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
//直接返回一个模板文件的解析结果,在页面上显示,同步执行
string ShowHtml = string.Empty;
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
//直接获取 /Templet/List.htm 模板文件的解析结果
ShowHtml = TempletHandler.GetFileString("/Templet/List.htm", TempletHandler.GetRunTime());
this.Response.Write(ShowHtml);//在页面上显示出来
//这个函数的意义是 获取/Templet/List.htm模板的解析结果 写到/CreatPage/List.shtml 文件内 , 同步执行
//TempletHandler.WriteFile("/Templet/List.htm", "/CreatPage/List.shtml", TempletHandler.GetRunTime());
}
所有的逻辑处理 分页 什么都没有 全部封装在 TempletHandler.GetFileString 函数内,看代码
case
@"
/Templet/List.htm
"
:
BackObject
=
new
ArticleTempletList();
这说明对模板文件 /Templet/List.htm 的处理类是 ArticleTempletList 类 。对某个模板的处理类 都必须要继承AsynchBaseTempletHandler 类, 为了继承了这个类,只要自己实现两个抽象函数,Page_Load,和Page_Init 就可以。ArticleTempletList类如何实现分页。如何显示数据,在这里就不详细的说明。大家可以下载demo看看。我要说明的是ArticleTempletList类的执行步骤。
文章模板列表类
#region 文章模板列表类
public sealed class ArticleTempletList : AsynchBaseTempletHandler, IRequiresSessionState
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
初试数据#region 初试数据
public const string TempletFilePath = @"/Templet/List.htm";//这个类解析模板的路径
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
private int _PageCount = 12;//一页要显示的数量,加上一个默认数值
private int _PageIndex = 1;//默认的页码
private int _PageAll = 1;//共有的页数
private int _AllCount = 1;//共有的行数
private IList<ArticleListNews> PLN; //临时集合变量
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
private int Count = 0;//记数变量
private HttpContext context;//上下文对象
#endregion
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
Page_Load事件函数#region Page_Load事件函数
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// Page_Load事件函数
/// </summary>
/// <param name="objILabel">不需要改变</param>
/// <param name="ShowText">不需要改变</param>
protected override void Page_Load(ref TempletReplet objILabel, HttpContext _context)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
获取变量#region 获取变量
//只有在这个函数内,异步处理事件才可以获取到上下文对象
this.context = _context;//这也是异步页面或http异步处理程序的一个问题 必须要保留连接对象
#endregion
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
使用模板部分,可以不使用#region 使用模板部分,可以不使用
objILabel = new ArticleTempletList();//这里必须要new一下,应为this里的Page_Init 没有执行
objILabel.ReadFilePath = _context.Server.MapPath(TempletFilePath);
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
Encoding gb2312 = Encoding.GetEncoding("gb2312");
_context.Response.ContentEncoding = gb2312;
#endregion
}
#endregion
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
标签函数执行体#region 标签函数执行体
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
获取一页要从数据库内获取的行数 获取一页的数据#region 获取一页要从数据库内获取的行数 获取一页的数据
public string DataBind(string PageItems)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
获取url上的变量#region 获取url上的变量
if (context.Request.QueryString.Count > 0)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
try
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
this._PageIndex = Convert.ToInt32(context.Request.QueryString[0]);
}
catch
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
this._PageIndex = 1;
}
}
#endregion
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
this._PageCount = Convert.ToInt32(PageItems);//获取分页 一页要显示多少数据
this.Count = 0;//初始化
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
//获取数据
ArticleNewsInterface AN =new ArticleSqlDataProvider();
this.PLN = AN.ArticleList(this._PageCount, this._PageIndex, ref _PageAll, ref this._AllCount);//获取数据
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
//return null;
return "输出时间刻度是为了说明缓存标签的执行与否:<b><font style='color: blue;'>" + DateTime.Now.Ticks.ToString() + "</font></b>";
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
#endregion
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
显示一行#region 显示一行
public string ShowItem(string value, string TitleLen)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
int _TitleLen = Convert.ToInt32(TitleLen);
ArticleListNews pn = null;
StringBuilder SB = new StringBuilder(value);
int i = this.GetCount();
if (i < this.PLN.Count)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
pn = this.PLN[i];
SB.Replace("#ID#",pn.ArticleId.ToString());
SB.Replace("#Title#", "<a href='/Page/Article.aspx?id=" + pn.ArticleId.ToString() + "' target='_blank'>" + ClassLibrary.ControlString.GetLimitStr(pn.Title, _TitleLen) + "</a>");
SB.Replace("#Time#", pn.AddTime.Year.ToString() + "-" + pn.AddTime.Month.ToString() + "-" + pn.AddTime.Day.ToString());
}
else
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
SB.Replace("#ID#","");
SB.Replace("#Title#", "");
SB.Replace("#Time#", "");
}
return SB.ToString();
}
#endregion
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
显示分页信息#region 显示分页信息
public string ShowPageIndex()
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
int shang = this._PageIndex - 1, xia = this._PageIndex + 1;
if (shang < 1) shang = 1;
if (xia > this._PageAll) xia = this._PageAll;
return "<a href='/List.aspx?PageIndex=1' class='hei'>首页</a> <a href='/List.aspx?PageIndex=" + shang.ToString() + "' class='hei'>上一页</a> <a href='/List.aspx?PageIndex=" + xia.ToString() + "' class='hei'>下一页</a> <a href='/List.aspx?PageIndex=" + this._PageAll.ToString() + "' class='hei'>尾页</a> 共"+ this._AllCount.ToString() +" 行 , " + this._PageAll.ToString() + " 页 这是 " + this._PageIndex.ToString() + " 页 ";
}
#endregion
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
#endregion
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
标签,标签解析函数影射事件#region 标签,标签解析函数影射事件
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// 标签,标签解析函数影射事件
/// </summary>
protected override void Page_Init(ref TempletReplet objILabel)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
//这个函数只执行一次
//如果要使用模板。那么写这个函数。要是不用模板,那么让这个函数为空就可以
ArticleTempletList.AddLabel("数据绑定", new Run_1(this.DataBind),1, ref objILabel);
ArticleTempletList.AddLabel("显示一行", new Run_2(this.ShowItem), 2, ref objILabel);
ArticleTempletList.AddLabel("显示分页信息", new Run_0(this.ShowPageIndex), 0, ref objILabel);
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
// {$显示分页信息::缓存$} 和函数 ShowPageIndex() 结合,做影射
// 标签有几个参数就使用 Run_0 ----- Run_20 不同的构造器 后面的参数数目也做对应修改
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
}
#endregion
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
返回记数变量#region 返回记数变量
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// 返回记数变量
/// </summary>
/// <returns></returns>
private int GetCount()
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
int i = this.Count;
this.Count++;
return i;
}
#endregion
}
#endregion
protected override void Page_Load(ref TempletReplet objILabel, HttpContext _context)函数首先执行,并且每次都执行,在执行他的时候,如果是第一次执行,在内部调用protected override void Page_Init(ref TempletReplet objILabel)
函数,如果不是第一次执行,就不调用Page_Init函数。
Page_Load 执行完毕以后,按模板上的标签排列的先后次序,依次执行各个标签的影射函数,也就是 标签函数执行体 内的函数。所以标签的影射函数有一个相互依赖的关系。先是执行获取数据和数据绑定函数,在执行显示一行的函数,应为有12个标签,所以执行了12次,最后在执行显示分页的函数。到这得说一下原先定义为系统的loop标签
{$loop(3,0,alter)$} {$/loop$}
{$BlockItem$} {$/BlockItem$} :默认的循环行标签对
{$BlockAlterItem$} {$/BlockAlterItem$} :交替循环行标签对
{$BlockPatch$} {$/BlockPatch$} :默认补充行标签对
{$BlockAlterPatch$} {$/BlockAlterPatch$} :交替补充行标签对
现在已经被取消了,应为进入2.0以后。普遍使用范型对象,用dt传数据远没有使用范型集合好。所以取消了loop循环相关的代码,也取消了识别
1。 dt[5][name] : 表示取Datatable内5行name列上的数值,第一个为数字,第二个是列名。
2。 dt[name] : 在loop循环外表示取Datatable内0行name列上的数值。
这样特殊表示的定义。并且用现在的方法可以获得更灵活的显示方式,更快的列表显示方式。具体看 ArticleTempletList 类和/Templet/List.htm 模板。
标签{$数据绑定:12::缓存$}。这里决定一页获取12条数据,那么下面放上12个 {$显示一行$} 标签就好了 数目不要错。要是想改变分页数量 只要上下的数量一致就可以。
<!--
放上12个 显示一行 标签就好了 和上面 数据绑定 标签内数据一致不要错了
-->
{$显示一行:
<
tr
bgcolor
='#FFFFFF'
><
td
align
='center'
><
img
src
='/Images/p1.gif'
/></
td
><
td
>
#ID#
</
td
><
td
>
#Title#
</
td
><
td
>
#Time#
</
td
></
tr
>
:20::缓存$}
{$显示一行:
<
tr
bgcolor
='#EFF0DB'
><
td
align
='center'
><
img
src
='/Images/p2.gif'
/></
td
><
td
>
#ID#
</
td
><
td
>
#Title#
</
td
><
td
>
#Time#
</
td
></
tr
>
:20::缓存$}
{$显示一行:
<
tr
bgcolor
='#FFFFFF'
><
td
align
='center'
><
img
src
='/Images/p3.gif'
/></
td
><
td
>
#ID#
</
td
><
td
>
#Title#
</
td
><
td
>
#Time#
</
td
></
tr
>
:20::缓存$}
{$显示一行:
<
tr
bgcolor
='#EFF0DB'
><
td
align
='center'
><
img
src
='/Images/p4.gif'
/></
td
><
td
>
#ID#
</
td
><
td
>
#Title#
</
td
><
td
>
#Time#
</
td
></
tr
>
:20::缓存$}
{$显示一行:
<
tr
bgcolor
='#FFFFFF'
><
td
align
='center'
><
img
src
='/Images/p5.gif'
/></
td
><
td
>
#ID#
</
td
><
td
>
#Title#
</
td
><
td
>
#Time#
</
td
></
tr
>
:20::缓存$}
{$显示一行:
<
tr
bgcolor
='#EFF0DB'
><
td
align
='center'
><
img
src
='/Images/p6.gif'
/></
td
><
td
>
#ID#
</
td
><
td
>
#Title#
</
td
><
td
>
#Time#
</
td
></
tr
>
:20::缓存$}
{$显示一行:
<
tr
bgcolor
='#FFFFFF'
><
td
align
='center'
><
img
src
='/Images/p7.gif'
/></
td
><
td
>
#ID#
</
td
><
td
>
#Title#
</
td
><
td
>
#Time#
</
td
></
tr
>
:20::缓存$}
{$显示一行:
<
tr
bgcolor
='#EFF0DB'
><
td
align
='center'
><
img
src
='/Images/p8.gif'
/></
td
><
td
>
#ID#
</
td
><
td
>
#Title#
</
td
><
td
>
#Time#
</
td
></
tr
>
:20::缓存$}
{$显示一行:
<
tr
bgcolor
='#FFFFFF'
><
td
align
='center'
><
img
src
='/Images/p9.gif'
/></
td
><
td
>
#ID#
</
td
><
td
>
#Title#
</
td
><
td
>
#Time#
</
td
></
tr
>
:20::缓存$}
{$显示一行:
<
tr
bgcolor
='#EFF0DB'
><
td
align
='center'
><
img
src
='/Images/p10.gif'
/></
td
><
td
>
#ID#
</
td
><
td
>
#Title#
</
td
><
td
>
#Time#
</
td
></
tr
>
:20::缓存$}
{$显示一行:
<
tr
bgcolor
='#FFFFFF'
><
td
align
='center'
><
img
src
='/Images/p11.gif'
/></
td
><
td
>
#ID#
</
td
><
td
>
#Title#
</
td
><
td
>
#Time#
</
td
></
tr
>
:20::缓存$}
{$显示一行:
<
tr
bgcolor
='#EFF0DB'
><
td
align
='center'
><
img
src
='/Images/p12.gif'
/></
td
><
td
>
#ID#
</
td
><
td
>
#Title#
</
td
><
td
>
#Time#
</
td
></
tr
>
:20::缓存$}
20表示标题长度是20 每个标签都要求缓存。这样我门可以精确控制到每个具体的行,每个行的显示长度。将具有更大的灵活性。
在说一下标签介绍:
在成对特殊符号{$ 和 $} 之间的内容为标签
如
{$标签名:参数1:参数2:参数3::字符10:缓存$}
{$标签名:参数1:参数2::字节10$}
在标签内部通过 :: 符号把标签分为 标签参数部分 和 命令控制部分 两块
{$标签名:参数1:参数2:参数3::字节10$}
标签参数部分 :: 命令控制部分
在这两部分内以:号分割标签参数部分和命令控制部分
标签参数部分: 第一个为标签名,就是标签解析函数的对应名称,以后的为函数的参数,标签解析函数接受全部参数类型为string类型的,参数个数为0--20个的函数,返回类型为string类型.命令控制部分: 现在只有两个长度命令符号 字符 和 字节 ,后带一个数字参数,表示截取标签解析结果的长度。一个缓存标记,表示标签参与缓存机制。完整的标签表示为 {$标签名:参数1:参数2:参数3::字符10:字节10:缓存:输出$} 。 输出表示强制发送输出缓冲中的内容,一般不需要使用这个。但是显示过长的内容的时候,有时需要。
最后说一下缓存,原先只是缓存解析数据。现在不但可以缓存解析数据,还可以缓存执行结果。缓存执行结果我用cache保留。是为了在内存压力过大的时候自动释放。有一个特殊的标签 {$ControlCache:缓存1:类型global:时间30:数量200$} 他必须是第一个出现的标签,并且名字叫ControlCache ,有四个参数 ,参数顺序必须按规定排列。才可以。我把他的语法规则都定死了,错一步也不可以。是为了自己不写很多的检测代码。也算是一个偷懒的方法吧 :)
缓存1 | 参数为1表示起用执行结果缓存 参数为0 表示不起用执行结果缓存 |
类型global | 参数为global表示缓存的是整个页面 只要不是global,是其他不为空的数据表示缓存的是一个个的动态标签 |
时间30 | 表示要缓存的秒数 |
数量200 | 表示只接受200个不同的url上的参数的缓存请求,多出的将不在缓存,是为了节省内存,让你可以更好的控制 |
{$标签名:参数1:参数2:参数3::字符10:字节10:缓存$} 这表示在局部页面缓存的时候,这个标签参与缓存。看文章正文显示模板页 /Templet/ShowArticle.htm 他的第一个标签{$ControlCache:缓存1:类型非global就可以:时间30:数量200$} 表示本页执行的是局部缓存,应为我假设需求里文章浏览量是不可以模糊的,必须每次都真实获取到。所以只能用局部缓存。下面的 {$Title::缓存$} 获取头 {$Content::缓存$} 获取正文,和获取数据标签{$数据绑定::缓存$} 一样全部缓存上了。但是标签 {$BrowerCount$} 获取浏览量不参与页面局部缓存,这个标签影射的函数必须每次都执行,获取到精确的浏览量数据。具体请看ShowArticleTemplet 类
文章正文显示类
#region 文章正文显示类
public sealed class ShowArticleTemplet : AsynchBaseTempletHandler, IRequiresSessionState
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
初试数据#region 初试数据
public const string TempletFilePath = @"/Templet/ShowArticle.htm";//这个类解析模板的路径
private int ArticleId = 0;//文章id
private ShowArticle AN; //临时集合变量
private ArticleNewsInterface ANIF;//数据提供者接口
#endregion
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
Page_Load事件函数#region Page_Load事件函数
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// Page_Load事件函数
/// </summary>
/// <param name="objILabel">不需要改变</param>
/// <param name="ShowText">不需要改变</param>
protected override void Page_Load(ref TempletReplet objILabel, HttpContext _context)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
获取变量#region 获取变量
//只有在这个函数内,异步处理事件才可以获取到上下文对象
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
获取url上的变量#region 获取url上的变量
if (_context.Request.QueryString.Count > 0)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
try
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
this.ArticleId = Convert.ToInt32(_context.Request.QueryString[0]);
}
catch
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
_context.Response.Write("获取参数错误");
_context.Response.End();
}
}
else
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
_context.Response.Write("没有获取到参数");
_context.Response.End();
}
#endregion
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
this.ANIF = new ArticleSqlDataProvider();//获得数据提供者对象
//在Page_Load里获得对象,不在DataBind内。是应为有一个非缓存标签 ,每次都要用这个对象。
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
#endregion
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
使用模板部分,可以不使用#region 使用模板部分,可以不使用
objILabel = new ShowArticleTemplet();//这里必须要new一下,应为this里的Page_Init 没有执行
objILabel.ReadFilePath = _context.Server.MapPath(TempletFilePath);
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
Encoding gb2312 = Encoding.GetEncoding("gb2312");
_context.Response.ContentEncoding = gb2312;
#endregion
}
#endregion
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
标签函数执行体#region 标签函数执行体
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
获取一页要从数据库内获取的行数 获取一页的数据#region 获取一页要从数据库内获取的行数 获取一页的数据
public string DataBind()
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
this.AN = ANIF.ShowArticle(this.ArticleId);//获取文章显示的数据,不包括浏览次数
//return null;
return "输出时间刻度是为了说明缓存标签的执行与否:<b><font style='color: blue;'>" + DateTime.Now.Ticks.ToString()+"</font></b>";
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
#endregion
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
显示标题#region 显示标题
public string Title()
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
return this.AN.Title;
}
#endregion
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
显示时间#region 显示时间
public string AddTime()
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
return this.AN.AddTime.Year.ToString() + "-" + this.AN.AddTime.Month.ToString() + "-" + this.AN.AddTime.Day.ToString();
}
#endregion
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
显示正文#region 显示正文
public string Content()
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
return this.AN.Content;
}
#endregion
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
显示浏览次数,这个函数是对应非缓存标签的#region 显示浏览次数,这个函数是对应非缓存标签的
public string BrowerCount()
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
return this.ANIF.GetArticleBrowerCount(this.ArticleId).ToString();
}
#endregion
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
#endregion
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
标签,标签解析函数影射事件#region 标签,标签解析函数影射事件
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// 标签,标签解析函数影射事件
/// </summary>
protected override void Page_Init(ref TempletReplet objILabel)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
//这个函数只执行一次
//如果要使用模板。那么写这个函数。要是不用模板,那么让这个函数为空就可以
ShowArticleTemplet.AddLabel("数据绑定", new Run_0(this.DataBind), 0, ref objILabel);
ShowArticleTemplet.AddLabel("Title", new Run_0(this.Title), 0, ref objILabel);
ShowArticleTemplet.AddLabel("AddTime", new Run_0(this.AddTime), 0, ref objILabel);
ShowArticleTemplet.AddLabel("Content", new Run_0(this.Content), 0, ref objILabel);
ShowArticleTemplet.AddLabel("BrowerCount", new Run_0(this.BrowerCount), 0, ref objILabel);
}
#endregion
}
#endregion
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
有个不好的实现是在缓存数量处理上,控制缓存数量是必须的,但规则是先访问的先缓存,等缓存数量满了以后就不在接受缓存请求,这样的算法实现起来是最简单的。遗憾的是现在没有实现查找浏览量最大页面参数的算法,也没有按浏览量自动更新缓存的算法。要想实现上面的功能,我的思路是做一个http处理模块,加在context.EndRequest += new EventHandler(this.BaseModuleRewriter_AuthorizeRequest); 事件上,由于每次访问缓存页面都要执行这个http处理模块,记录访问参数,对缓存页的访问参数数量做排序,所以我的想法是要避免对内存数据的锁,记录数据要写在一个环型队列或者两个堆栈上,用异步事件来处理。实现对内存数据的读写分离。排序结果要做序列化,保留在磁盘上。所以有必要对缓存的算法做升级。现在看来更好的做法是把换存控制写成抽象类,开放处来 根据具体的需求自由改变缓存控制类,这样才是更好的。
新加了一种url重写的方法。直接分析文件名,选用不同的处理类。具体看demo内
在配置文件上需要加上
<
appSettings
>
<!--
是否动态检测模板,和保留文件格式
-->
<
add
key
="ExamineTemplate"
value
="1"
/>
</
appSettings
>
为1 表示 每次都检测模板文件是否更新,保留输出内容的排版格式。
为0 表示 不每次都检测文件是否更新,不保留输出内容的输出格式。
这样可以速度更快 ,输出的内容更小。如果不在配置文件加上上述内容,那默认是按1来执行。如果要在页面内使用ajax,只要把ajax类注册放在逻辑层。 调用放在页面上就可以了。就象在静态页上使用ajax一样就可以。
本文来自:http://www.cnblogs.com/vitality2007/archive/2008/01/21/659361.html#1046804