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,里面用到了调用手机摄像头扫一扫二维码的功能,记得以前某个项目里写有的,但是找不到之前那个项目源码了,想复制粘贴也复制不了了,只好对着微信的那个开发文 ...
随机推荐
- linux c编程:信号(二) alarm和pause函数
使用alarm函数可以设置一个定时器,在将来的某个时刻该定时器超时.当定时器超时后,产生SIGALRM信号.如果忽略或不捕捉此信号,则其默认动作是终止调用该alarm函数的进程 #include< ...
- Vue-cli创建项目从单页面到多页面3-关于将打包后的项目文件不放在根目录下
关于将打包后的项目文件不放在根目录下 有时候,我们总是需要这样的设置:希望将打包后的文件放在a.b.com/somepath/这样一个路径下. 然而在vue-cli创建的项目中,默认的打包路径中的静态 ...
- 每天一个Linux命令(20)find命令_exec参数
find命令的exec参数,用于find查找命令完成以后的后续操作. (1)用法: 用法: [find命令] [-exec 其他命令 {} \;] (2)功能: 功能:-exec ...
- MySQL创建用户并授权及撤销用户权限
这篇文章主要介绍了MySQL创建用户并授权及撤销用户权限.设置与更改用户密码.删除用户等等,需要的朋友可以参考下 MySQL中创建用户与授权的实现方法. 运行环境:widnows xp profess ...
- HTML URL 编码:请参阅:http://www.w3school.com.cn/tags/html_ref_urlencode.html
http://www.w3school.com.cn/tags/html_ref_urlencode.html
- Delphi中 为DBNavigator的按钮加中文
Delphi中 为DBNavigator的按钮加中文 /*Delphi中数据库控件DBNavigator使用起来不错,但是按钮上“+”.“-”等含义对于中国的用户不习惯,甚至不知道是什么含义.改成相应 ...
- linux 交换分区 swap
linux swap分区用来保证内存过载时也可以使用,是在磁盘级别对内存的一次扩展,swap分区必须是一个单独的分区 创建过程: 1.用fdisk 命令新建分区,在创建过程中通过L命令和t命令来调整分 ...
- ibatis的resultClass与resultMap 的区别
ibatis的resultClass与resultMap还是有很大的区别.以下是我碰到的一个问题. 配置文件写法如下: 1 sqlMap2 typeAlias alias="notice&q ...
- Python with MYSQL - sytax problem
Con= MySQLdb.connect(host=',db='test') cur=Con.cursor() cur.execute('insert into staff_daily(Date,Na ...
- javaScript-基础篇(一)
1.如何插入JS 使用<script>标签在HTML网页中插入JavaScript代码.注意, <script>标签要成对出现,并把JavaScript代码写在<scri ...