asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践
最近给SpaceBuilder增加OutputCache 时发现了一些问题,贴在这做个备忘,也方便遇到类似问题的朋友查阅。
目前SpaceBuilder表现层使用是asp.net mvc v1.0,使用了很多RenderAction(关于asp.net mvc的Partial Requests参见Partial Requests in ASP.NET MVC)。希望对于实时性要求不高的内容区域采用客户端缓存来提升性能同时也弥补一下RenderAction对性能的损失。
使用asp.net mvc自带的OutputCache Filter时发现了一个可怕的bug,在View中任何一个RenderAction设置OutputCache却影响了整个View。搜索发现确实是asp.net mvc目前已知的一个bug ,关于该问题的解决也有很多人提出了自己的方法。
关于asp.net mvc的缓存,Haacked写了两篇文章:
Donut Caching in ASP.NET MVC 介绍的是缓存整个页面,对于一部分内容禁用缓存,是在mvc中实现的WebForm的Substitution功能。存在以下弊端:当前一个View中有多个区域需要禁用缓存时使用比较麻烦,另外不能实现对页面的不同的区域使用不同的过期策略。
Donut Hole Caching in ASP.NET MVC介 绍的是我想要的功能,即只缓存页面的部分区域。但是弊端也非常明显:只能通过WebForm中的声明方式来使用用户控件(:),现在已经有点不适应这种方 式了,而且必须使用WebFormViewEngine),无法直接使用RenderPartial,而且还必须设置强类型的ViewPage,确保在用 户控件中的Model与View中的Model相同。使用太麻烦,限制也多。
Maarten Balliauw在 Creating an ASP.NET MVC OutputCache ActionFilterAttribute 和Extending ASP.NET MVC OutputCache ActionFilterAttribute - Adding substitution 也提出了一个完整的OutputCache解决方案。但是经测试启用客户端缓存时同样会产生与RenderAction同样的问题,还没有时间彻查这个问题,先把客户端缓存禁用,暂时使用服务器端缓存应付一阵。
以Maarten Balliauw的代码为原型,编写了SpaceBuilder的ActionOutputCacheAttribute:
publicclass ActionOutputCacheAttribute : ActionFilterAttribute { privatestatic MethodInfo _switchWriterMethod =typeof(HttpResponse).GetMethod("SwitchWriter", BindingFlags.Instance | BindingFlags.NonPublic); public ActionOutputCacheAttribute(int cacheDuration) { _cacheDuration = cacheDuration; } //目前还不能设置为Client缓存,会与OutputCache同样的问题 private CachePolicy _cachePolicy = CachePolicy.Server; privateint _cacheDuration; private TextWriter _originalWriter; privatestring _cacheKey; publicoverridevoid OnActionExecuting(ActionExecutingContext filterContext) { // Server-side caching? if (_cachePolicy == CachePolicy.Server || _cachePolicy == CachePolicy.ClientAndServer) { _cacheKey = GenerateCacheKey(filterContext); CacheContainer cachedOutput = (CacheContainer)filterContext.HttpContext.Cache[_cacheKey]; if (cachedOutput !=null) { filterContext.HttpContext.Response.ContentType = cachedOutput.ContentType; filterContext.Result =new ContentResult { Content = cachedOutput.Output }; } else { StringWriter stringWriter =new StringWriterWithEncoding(filterContext.HttpContext.Response.ContentEncoding); HtmlTextWriter newWriter =new HtmlTextWriter(stringWriter); _originalWriter = (TextWriter)_switchWriterMethod.Invoke(HttpContext.Current.Response, newobject[] { newWriter }); } } } publicoverridevoid OnResultExecuted(ResultExecutedContext filterContext) { // Server-side caching? if (_cachePolicy == CachePolicy.Server || _cachePolicy == CachePolicy.ClientAndServer) { if (_originalWriter !=null) // Must complete the caching { HtmlTextWriter cacheWriter = (HtmlTextWriter)_switchWriterMethod.Invoke(HttpContext.Current.Response, newobject[] { _originalWriter }); string textWritten = ((StringWriter)cacheWriter.InnerWriter).ToString(); filterContext.HttpContext.Response.Write(textWritten); CacheContainer container =new CacheContainer(textWritten, filterContext.HttpContext.Response.ContentType); filterContext.HttpContext.Cache.Add(_cacheKey, container, null, DateTime.Now.AddSeconds(_cacheDuration), System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.Normal, null); } } } privatestring GenerateCacheKey(ActionExecutingContext filterContext) { StringBuilder cacheKey =new StringBuilder("OutputCacheKey:"); // Controller + action cacheKey.Append(filterContext.Controller.GetType().FullName.GetHashCode()); if (filterContext.RouteData.Values.ContainsKey("action")) { cacheKey.Append("_"); cacheKey.Append(filterContext.RouteData.Values["action"].ToString()); } foreach (KeyValuePair<string, object> pair in filterContext.ActionParameters) { cacheKey.Append("_"); cacheKey.Append(pair.Key); cacheKey.Append("="); if (pair.Value !=null) cacheKey.Append(pair.Value.ToString()); else cacheKey.Append(string.Empty); } return cacheKey.ToString(); } privateclass CacheContainer { publicstring Output; publicstring ContentType; public CacheContainer(string data, string contentType) { Output = data; ContentType = contentType; } } publicenum CachePolicy { NoCache =, Client =, Server =, ClientAndServer = } }
{ 
encoding;


StringWriterWithEncoding publicclass StringWriterWithEncoding : StringWriter { Encoding encoding; public StringWriterWithEncoding(Encoding encoding) { this.encoding = encoding; } publicoverride Encoding Encoding { get{ return encoding; } } }
转自:http://www.cnblogs.com/mazq/archive/2009/05/30/1492298.html
asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践的更多相关文章
- Asp.net mvc web api 在项目中的实际应用
Asp.net mvc web api 在项目中的实际应用 前言:以下只是记录本人在项目中的应用,而web api在数据传输方面有多种实现方式,具体可根据实际情况而定! 1:数据传输前的加密,以下用到 ...
- 在Asp.net MVC 3 web应用程序中,我们会用到ViewData与ViewBag,对比一下:
Asp.net MVC中的ViewData与ViewBag ViewData ViewBag 它是Key/Value字典集合 它是dynamic类型对像 从Asp.net MVC 1 就有了 ASP. ...
- ASP.NET MVC 学习8、Controller中的Detail和Delete方法
参考:http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/examining-the-details-and ...
- ASP.NET MVC 学习4、Controller中添加SearchIndex页面,实现简单的查询功能
参考:http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/examining-the-edit-method ...
- [小技巧][ASP.Net MVC Hack] 使用 HTTP 报文中的 Header 字段进行身份验证
在一些 Web 系统中,身份验证是依靠硬件证书进行的:在电脑上插入 USB 证书,浏览器插件读取证书的相关信息,然后在发送 HTTP 登录请求时顺便在 Header 字段附加上身份信息.服务器端处理这 ...
- [转]Load ASP.NET MVC Partial Views Dynamically Using jQuery
本文转自:http://www.binaryintellect.net/articles/218ca630-ba50-48fe-af6e-6f754b5894aa.aspx Most of the t ...
- ASP.NET MVC Partial页输出JS
很多情况Partial是需要引用到JS的,通常做法是吧JS在引用Partial的页面中加入JS文件或者JS代码. 前阵子网上看到一段代码可以在Partial页面中添加JS,输出道引用页面. publi ...
- ASP.NET MVC系列:web.config中ConnectionString aspnet_iis加密与AppSettings独立文件
1. web.config中ConnectionString aspnet_iis加密 web.config路径:E:\Projects\Libing.Web\web.config <conne ...
- ASP.NET MVC做的微信WEBAPP中调用微信JSSDK扫一扫
今天做一个项目,是在微信上用的,微信WEB APP,里面用到了调用手机摄像头扫一扫二维码的功能,记得以前某个项目里写有的,但是找不到之前那个项目源码了,想复制粘贴也复制不了了,只好对着微信的那个开发文 ...
随机推荐
- Symfony3 查询搜索功能DQL语句like查询
//前台页面代码 <form method="GET" action="{{ path('staff_index') }}" name="sea ...
- java面试之数据库面试知识点【转】
1. 主键 超键 候选键 外键 主 键: 数据库表中对储存数据对象予以唯一和完整标识的数据列或属性的组合.一个数据列只能有一个主键,且主键的取值不能缺失,即不能为空值(Null). 超 键: 在关系中 ...
- Java找出一组数字的最大值
形如:int [] nums = {7,2,8,9,1,12}; 解一:两两比较并记录下标,下次比较拿上次比较的最大值和上次比较的下一个进行比较,循环一次找出最大值 /** * @author 马向峰 ...
- WORD表格数据运算技巧
我们经常会用WORD制作表格,有时表内的数据要运算的,WORD表格数据运算能力无法与EXCEL相比.但常见的乘除加减.相邻数据累加,将金额数字自动转成大写,WORD都能在表格内自动完成.下面以一个简单 ...
- 数据分析R语言(1)
无意中发现网上的一个数据分析R应用教程,看了几集感觉还不错,本文做一个学习笔记(知识点来源:视频内容+R实战+自己的理解),视频详细的信息请参考http://www.itao521.com/cours ...
- Effective java -- 9 并发/序列化
关于同步的问题,想弄明白java,同步不会是不行的.这不书弄完后还会从<java并发编程实战>和<java并发编程的艺术>选一本或者都看. 第六十六条:同步访问共享的可变数据说 ...
- CSS3自定义Checkbox特效
在线演示 本地下载
- CHAR,TCHAR,WCHAR 三者的区别与转换
#ifdef UNICODE typedef wchar_t TCHAR; #else typedef unsigned c ...
- mysql 在windows server下发生系统错误 1067, 进程意外终止的解决方法
mysql 在windows server下发生系统错误 1067, 进程意外终止,请检查系统盘下的windows目录下是否存在mysql的配置文件my.ini,如存在,将其删除或改名即可.
- 关于HttpURLConnection测试servlet
把数据POST给服务端后,一定要读取服务端的响应,这是必须的,否则服务端不处理.其实发送的数据被服务端接收后在缓冲中,并不是立即处理的.然后服务端把响应码和内容等反回给客户端.如果客户端只发送不接受, ...