目前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 ,关于该问题的解决也有很多人提出了自己的方法。
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 =0,
Client =1,
Server =2,
ClientAndServer =3
}
}