没有找到合适的产品?
联系客服协助选型:023-68661681
提供3000多款全球软件/控件产品
针对软件研发的各个阶段提供专业培训与技术咨询
根据客户需求提供定制化的软件开发服务
全球知名设计软件,显著提升设计质量
打造以经营为中心,实现生产过程透明化管理
帮助企业合理产能分配,提高资源利用率
快速打造数字化生产线,实现全流程追溯
生产过程精准追溯,满足企业合规要求
以六西格玛为理论基础,实现产品质量全数字化管理
通过大屏电子看板,实现车间透明化管理
对设备进行全生命周期管理,提高设备综合利用率
实现设备数据的实时采集与监控
利用数字化技术提升油气勘探的效率和成功率
钻井计划优化、实时监控和风险评估
提供业务洞察与决策支持实现数据驱动决策
原创|其它|编辑:郝浩|2009-09-22 10:19:25.000|阅读 505 次
概述:上一篇文章中我们提出了了片断缓存的基本方式,也就是构建HtmlHelper的扩展方法Cache,接受一个用于生成字符串的委托对象。在缓存命中时,则直接返回缓存中的字符串片断,否则则使用委托生成的内容。
# 界面/图表报表/文档/IDE等千款热门软控件火热销售中 >>
上一篇文章中我们提出了了片断缓存的基本方式,也就是构建HtmlHelper的扩展方法Cache,接受一个用于生成字符串的委托对象。在缓存命中时,则直接返回缓存中的字符串片断,否则则使用委托生成的内容。因此,缓存命中时委托的开销便节省了下来。不过这个方法并不实用,如果您要缓存大片的HTML,还需要准备一个Partial View,再用它来生成网页片段:
<%= Html.Cache(..., () => Html.Partial("MyPartialViewToCache")) %>
但是在实际开发过程中,我们最乐于看到的使用方法,应该只是使用某个标记来“围绕”一段现有的代码。也就是说,我们希望的API使用方式可能是这样的:
<% Html.Cache("cache_key", DateTime.Now.AddSeconds(10), () => { %> <% foreach (var article in Model.Articles) { %> <p><%= article.Body %></p> <% } %> <% }); %>
我们可以从这种“表现形式”上推断出这个Cache方法的签名:
public static void Cache( this HtmlHelper htmlHelper, string cacheKey, CacheDependency cacheDependencies, DateTime absoluteExpiration, TimeSpan slidingExpiration, Action action) { ... }
与前一个扩展相比,最后一个委托参数变成了Action,而不是Func<string>。这是因为ASP.NET页面在编译时,会将页面Cache块中的代码,编译为内容的输出方式——这点在之前的文章中已经有过比较详细的描述。不过有一点还是与之前相同的,我们要省下的是action委托的开销。也就是说,如果缓存命中,则不执行action。缓存没有命中,则执行action,获得action生成的字符串,加入缓存并输出。
看似比较简单,但这里有个问题:如之前的Func<string>参数,我们执行后自然可以获得一个字符串作为结果。但是现在是个action,执行后它又把内容输出到什么地方去,我们又该如何得到这里生成的字符串呢?根据页面输出行为,我们可以推断出页面上的内容是被写入一个HtmlTextWriter中的。那么,这个HtmlTextWriter又是如何生成的呢?
它是根据Page类型的CreateHtmlTextWriter方法生成的:
protected virtual HtmlTextWriter CreateHtmlTextWriter(System.IO.TextWriter tw) { ... }
在页面准备生成内容之前,Page会调用其CreateHtmlTextWriter来包装一个TextWriter,这个TextWriter一般即是由Response.Output暴露出来的HttpWriter对象。CreateHtmlTextWriter方法生成的HtmlTextWriter,便会交给Page的Render方法用于输出页面内容了。这便是我们的入手点,我们可以趁此机会在HtmlTextWriter和CreateHtmlTextWriter之间“插入”一个组件。这个组件除了将外部传入的数据传入内部的TextWriter以外,还有着“纪录”内容的功能:
internal class RecordWriter : TextWriter { public RecordWriter(TextWriter innerWriter) { this.m_innerWriter = innerWriter; } private TextWriter m_innerWriter; private List<StringBuilder> m_recorders = new List<StringBuilder>(); public override Encoding Encoding { get { return this.m_innerWriter.Encoding; } } public override void Write(char value) { ... } public override void Write(string value) { if (value != null) { this.m_innerWriter.Write(value); if (this.m_recorders.Count > 0) { foreach (var recorder in this.m_recorders) { recorder.Append(value); } } } } public override void Write(char[] buffer, int index, int count) { ... } public void AddRecorder(StringBuilder recorder) { this.m_recorders.Add(recorder); } public void RemoveRecorder(StringBuilder recorder) { this.m_recorders.Remove(recorder); } }
一个TextWriter有数十个可以覆盖的成员,但是一般情况下我们只需覆盖其中三个Write方法就可以了。以上代码用Write(string)作为示例,可以看出,如果RecordWriter中添加了Recorder之后,便会将外界写入的内容再交给Recorder一次。换句话说,如果我们希望纪录页面上写入Writer的内容,只要在RecordWriter里添加Recorder就可以了。当然,在此之前我们需要为视图页面“开启”缓存功能:
// 定义在CacheExtensions中 public static TextWriter CreateCacheWriter(this HtmlHelper htmlHelper, TextWriter writer) { var recordWriter = new RecordWriter(writer); htmlHelper.SetRecordWriter(recordWriter); return recordWriter; } // 定义在视图页面(aspx)中 <script runat="server"> protected override HtmlTextWriter CreateHtmlTextWriter(System.IO.TextWriter tw) { return base.CreateHtmlTextWriter(Html.CreateCacheWriter(tw)); } </script>
当然,在实际开发过程中不会在aspx中重写CreateHtmlTextWriter方法,我们往往会将其放在视图页面的共同基类中。例如在我的项目中,我就为所有的视图“开启”了这种纪录功能。由于在没有缓存的情况下这层薄薄的封装只是在做一个“转发”功能,因此不会带来性能问题。
此时,新的Cache方法便非常直观了:
public static void Cache( this HtmlHelper htmlHelper, string cacheKey, CacheDependency cacheDependencies, DateTime absoluteExpiration, TimeSpan slidingExpiration, Action action) { var cache = htmlHelper.ViewContext.HttpContext.Cache; var content = cache.Get(cacheKey) as string; var writer = htmlHelper.GetRecordWriter(); if (content == null) { var recorder = new StringBuilder(); writer.AddRecorder(recorder); action(); writer.RemoveRecorder(recorder); content = recorder.ToString(); cache.Insert(cacheKey, content, cacheDependencies, absoluteExpiration, slidingExpiration); } else { htmlHelper.Output.Write(content); } }
如果缓存没有命中,则我们会向RecordWriter中添加一个Recorder,然后再执行action委托,这样action中的所有内容便会被纪录下来。action执行完毕后,我们再摘除Recorder即可。现在Cache方法已经可用了,例如:
<%= DateTime.Now %> <br /> <% Html.Cache("now", DateTime.Now.AddSeconds(5), () => { %> <%= DateTime.Now %> <% }); %>
那么,Html.Cache能否嵌套呢?答案也是肯定的。
<%= DateTime.Now %> <br /> <% Html.Cache("now", DateTime.Now.AddSeconds(5), () => { %> <%= DateTime.Now %> <br /> <% Html.Cache("inner_now", DateTime.Now.AddSeconds(10), () => { %> <% Html.RenderPartial("CurrentTime"); %> <% }); %> <% }); %>
外层缓存块5秒后过期,内存缓存块10秒钟过期,因此在某一时刻(如第一次刷新后7秒后),您会发现页面上会出现这样的结果:
2009/9/21 15:36:10 2009/9/21 15:36:08 2009/9/21 15:36:03
我们的RecordWriter支持同时拥有多个recorder,您可以根据上面得出的结果来理解内外层循环是以何种顺序向RecordWriter添加Recorder的,这并不困难。
从代码中我们也可以发现,Cache块内部也可以直接使用Html.RenderPartial。您也可以在Cache块内部使用各种辅助方法,它们的结果会被一并缓存下来。
不过它们还是有“前提”的,至于这个前提是什么,我们下次在讨论吧。如果您想先睹为快,可以关注MvcPatch项目。
本站文章除注明转载外,均为本站原创或翻译。欢迎任何形式的转载,但请务必注明出处、不得修改原文相关链接,如果存在内容上的异议请邮件反馈至chenjj@evget.com
文章转载自:博客园面对“数字中国”建设和中国制造2025战略实施的机遇期,中车信息公司紧跟时代的步伐,以“集约化、专业化、标准化、精益化、一体化、平台化”为工作目标,大力推进信息服务、工业软件等核心产品及业务的发展。在慧都3D解决方案的实施下,清软英泰建成了多模型来源的综合轻量化显示平台、实现文件不失真的百倍压缩比、针对模型中的大模型文件,在展示平台上进行流畅展示,提升工作效率,优化了使用体验。
本站的模型资源均免费下载,登录后即可下载。模型仅供学习交流,勿做商业用途。
本站的模型资源均免费下载,登录后即可下载。模型仅供学习交流,勿做商业用途。
本站的模型资源均免费下载,登录后即可下载。模型仅供学习交流,勿做商业用途。
服务电话
重庆/ 023-68661681
华东/ 13452821722
华南/ 18100878085
华北/ 17347785263
客户支持
技术支持咨询服务
服务热线:400-700-1020
邮箱:sales@evget.com
关注我们
地址 : 重庆市九龙坡区火炬大道69号6幢
慧都科技 版权所有 Copyright 2003-
2025 渝ICP备12000582号-13 渝公网安备
50010702500608号