最近给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 ActionFilterAttributeExtending ASP.NET MVC OutputCache ActionFilterAttribute - Adding substitution   也提出了一个完整的OutputCache解决方案。但是经测试启用客户端缓存时同样会产生与RenderAction同样的问题,还没有时间彻查这个问题,先把客户端缓存禁用,暂时使用服务器端缓存应付一阵。

以Maarten Balliauw的代码为原型,编写了SpaceBuilder的ActionOutputCacheAttribute:

 

asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_ide
asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_html_02    publicclass ActionOutputCacheAttribute : ActionFilterAttribute asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_缓存_03asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_ide_04    asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_05{ asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06        privatestatic MethodInfo _switchWriterMethod =typeof(HttpResponse).GetMethod("SwitchWriter", BindingFlags.Instance | BindingFlags.NonPublic); asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06        public ActionOutputCacheAttribute(int cacheDuration) asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_asp.net_09asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_10        asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_05{ asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06            _cacheDuration = cacheDuration; asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_缓存_13        } asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06        //目前还不能设置为Client缓存,会与OutputCache同样的问题 asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06        private CachePolicy _cachePolicy = CachePolicy.Server; asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06        privateint _cacheDuration; asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06        private TextWriter _originalWriter; asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06        privatestring _cacheKey; asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06        publicoverridevoid OnActionExecuting(ActionExecutingContext filterContext) asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_asp.net_09asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_10        asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_05{ asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06            // Server-side caching? asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06            if (_cachePolicy == CachePolicy.Server || _cachePolicy == CachePolicy.ClientAndServer) asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_asp.net_09asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_10            asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_05{ asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06                _cacheKey = GenerateCacheKey(filterContext); asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06                CacheContainer cachedOutput = (CacheContainer)filterContext.HttpContext.Cache[_cacheKey]; asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06                if (cachedOutput !=null) asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_asp.net_09asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_10                asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_05{ asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06                    filterContext.HttpContext.Response.ContentType = cachedOutput.ContentType; asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_asp.net_09asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_10                    filterContext.Result =new ContentResult asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_05{ Content = cachedOutput.Output }; asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_缓存_13                } asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06                else asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_asp.net_09asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_10                asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_05{ asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06                    StringWriter stringWriter =new StringWriterWithEncoding(filterContext.HttpContext.Response.ContentEncoding); asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06                    HtmlTextWriter newWriter =new HtmlTextWriter(stringWriter); asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_asp.net_09asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_10                    _originalWriter = (TextWriter)_switchWriterMethod.Invoke(HttpContext.Current.Response, newobject[] asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_05{ newWriter }); asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_缓存_13                } asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_缓存_13            } asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_缓存_13        } asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06        publicoverridevoid OnResultExecuted(ResultExecutedContext filterContext) asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_asp.net_09asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_10        asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_05{ asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06            // Server-side caching? asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06            if (_cachePolicy == CachePolicy.Server || _cachePolicy == CachePolicy.ClientAndServer) asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_asp.net_09asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_10            asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_05{ asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06                if (_originalWriter !=null) // Must complete the caching asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_asp.net_09asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_10                asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_05{ asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_asp.net_09asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_10                    HtmlTextWriter cacheWriter = (HtmlTextWriter)_switchWriterMethod.Invoke(HttpContext.Current.Response, newobject[] asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_05{ _originalWriter }); asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06                    string textWritten = ((StringWriter)cacheWriter.InnerWriter).ToString(); asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06                    filterContext.HttpContext.Response.Write(textWritten); asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06                    CacheContainer container =new CacheContainer(textWritten, filterContext.HttpContext.Response.ContentType); asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06                    filterContext.HttpContext.Cache.Add(_cacheKey, container, null, DateTime.Now.AddSeconds(_cacheDuration), System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.Normal, null); asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_缓存_13                } asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_缓存_13            } asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_缓存_13        } asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06        privatestring GenerateCacheKey(ActionExecutingContext filterContext) asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_asp.net_09asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_10        asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_05{ asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06            StringBuilder cacheKey =new StringBuilder("OutputCacheKey:"); asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06            // Controller + action asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06            cacheKey.Append(filterContext.Controller.GetType().FullName.GetHashCode()); asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06            if (filterContext.RouteData.Values.ContainsKey("action")) asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_asp.net_09asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_10            asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_05{ asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06                cacheKey.Append("_"); asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06                cacheKey.Append(filterContext.RouteData.Values["action"].ToString()); asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_缓存_13            } asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06            foreach (KeyValuePair<string, object> pair in filterContext.ActionParameters) asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_asp.net_09asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_10            asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_05{ asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06                cacheKey.Append("_"); asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06                cacheKey.Append(pair.Key); asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06                cacheKey.Append("="); asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06                if (pair.Value !=null) asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06                    cacheKey.Append(pair.Value.ToString()); asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06                else asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06                    cacheKey.Append(string.Empty); asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_缓存_13            } asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06            return cacheKey.ToString(); asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_缓存_13        } asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06        privateclass CacheContainer asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_asp.net_09asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_10        asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_05{ asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06            publicstring Output; asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06            publicstring ContentType; asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06            public CacheContainer(string data, string contentType) asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_asp.net_09asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_10            asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_05{ asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06                Output = data; asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06                ContentType = contentType; asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_缓存_13            } asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_缓存_13        } asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06        publicenum CachePolicy asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_asp.net_09asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_10        asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_05{ asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06            NoCache =0, asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06            Client =1, asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06            Server =2, asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06            ClientAndServer =3 asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_缓存_13        } asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_ide_135    }
asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_ide

asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_html_02    asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_05{ asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_05 encoding; asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_缓存_13asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06        asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_05asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_05asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_缓存_13asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_ide_135

 

 

asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_html_147asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_148StringWriterWithEncoding asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_html_02    publicclass StringWriterWithEncoding : StringWriter asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_缓存_03asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_ide_04    asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_05{ asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06        Encoding encoding; asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06        public StringWriterWithEncoding(Encoding encoding) asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_asp.net_09asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_10        asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_05{ asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06            this.encoding = encoding; asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_缓存_13        } asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_06        publicoverride Encoding Encoding asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_asp.net_09asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_10        asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_05{ asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_asp.net_09asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_10            getasp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_mvc_05{ return encoding; } asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_缓存_13        } asp.net mvc Partial OutputCache 在SpaceBuilder中的应用实践_ide_135    }