软件下载 | 资讯教程 | 最近更新 | 下载排行 | 一键转帖 | 发布投稿
您的位置:最火下载站 > 网络编程 > ASP.NET > ASP.NET MVC 1.0 StaticCacheAttribute

ASP.NET MVC 1.0 StaticCacheAttribute

静态化缓存 和 OutputCache 不同,它适用于那些有大量可访问页面的网站(比如新闻站点)。要知道我们的网站每天都会被很多智商不高的傻蜘蛛和灌水机做全身检查,这些家伙往往会深度遍历 n 多的页面,很容易导致我们有限的 "动态缓存" 被频繁刷新,失去了缓存应有的价值。还有一点,静态缓存往往独立存储,可以是多台 Memchaced 或者 DFS 系统,如此在一个负载均衡环境里,这种跨进程和机器的缓存模式会更有效。

我们首先定义一个缓存接口,以便日后可以实现多种缓存策略。

public interface IStaticCache
{
void Set(string key, object value, DateTime absoluteExpiration);
void Set(string key, object value, TimeSpan slidingExpiration);
T Get<T>(string key);
void Delete(string key);
}

接下来,我们要拦截 ActionResult Render 输出,将其保存到缓存中,并在下一次访问时使用。原理很简单,就是调用 Response.SwitchWriter 更换输出器。

public sealed class HttpResponse
{
internal TextWriter SwitchWriter(TextWriter writer)
{
TextWriter writer2 = this._writer;
this._writer = writer;
return writer2;
}
}

虽然是 internal,用反射还是很容易达成目的。

private TextWriter SwitchWriter(TextWriter writer)
{
var method = typeof(HttpResponse).GetMethod("SwitchWriter",
BindingFlags.NonPublic | BindingFlags.Instance);

return method.Invoke(HttpContext.Current.Response, new object[] { writer }) as TextWriter;
}

接下来,我们拦截输出结果。

public class StaticCacheAttribute : ActionFilterAttribute
{
private TextWriter origin;
private StringWriter writer;

public override void OnActionExecuting(ActionExecutingContext filterContext)
{
writer = new StringWriter();
origin = SwitchWriter(writer);
}

public override void OnResultExecuted(ResultExecutedContext filterContext)
{
if (writer != null)
{
writer.Flush();
var html = writer.ToString();

// 向客户端输出内容。
SwitchWriter(origin);
filterContext.HttpContext.Response.Write(html);
filterContext.HttpContext.Response.Flush();
filterContext.HttpContext.Response.End();
}
}
}

拦截以后,别忘了将原来的 TextWriter 改回去,同时输出本次被拦截的内容。

有了这个数据,接下来就可以使用缓存了。有一点需要注意,在命中缓存后,应该用 "filterContext.Result = new EmptyResult();" 阻止 Action Method 执行,否则就失去缓存的目的了。

原因: ControllerActionInvoker.InvokeActionMethodFilter() 通过判断 "preContext.Result != null" 来决定是否执行后续调用。

public class ControllerActionInvoker
{
internal static ActionExecutedContext InvokeActionMethodFilter(...)
{
filter.OnActionExecuting(preContext);

if (preContext.Result != null)
{
return new ActionExecutedContext(...)
{
Result = preContext.Result
};
}

...
}
}

好了,看一个完整的 StaticCacheAttribute 示例。

public class StaticCacheAttribute : ActionFilterAttribute
{
private TextWriter origin;
private StringWriter writer;
private String key;

public StaticCacheAttribute()
{
Duration = 60; // 秒
ContentType = "text/html";
Cache = StaticHtmlCache.Instance;

this.Order = -1;
}

public int Duration { get; set; }
public string ContentType { get; set; }
public IStaticCache Cache { get; set; }

private TextWriter SwitchWriter(TextWriter writer)
{
var method = typeof(HttpResponse).GetMethod("SwitchWriter",
BindingFlags.NonPublic | BindingFlags.Instance);

return method.Invoke(HttpContext.Current.Response, new object[] { writer }) as TextWriter;
}

public override void OnActionExecuting(ActionExecutingContext filterContext)
{
key = filterContext.HttpContext.Request.RawUrl;
var html = Cache.Get<string>(key);

if (String.IsNullOrEmpty(html))
{
writer = new StringWriter();
origin = SwitchWriter(writer);
}
else
{
filterContext.Result = new EmptyResult();

filterContext.HttpContext.Response.ContentType = ContentType;
filterContext.HttpContext.Response.Write(html);
filterContext.HttpContext.Response.Flush();
filterContext.HttpContext.Response.End();
}
}

public override void OnResultExecuted(ResultExecutedContext filterContext)
{
if (writer != null)
{
writer.Flush();
var html = writer.ToString();

// 将结果存入缓存器。
Cache.Set(key, html, DateTime.Now.AddSeconds(Duration));

// 向客户端输出内容。
SwitchWriter(origin);
filterContext.HttpContext.Response.Write(html);
filterContext.HttpContext.Response.Flush();
filterContext.HttpContext.Response.End();
}
}
}

相关阅读
栏目导航
推荐软件