ASP.NET MVC 3中的OutputCache行为

10

我只是在 ASP.NET MVC 3 的 RC 版中测试输出缓存。

不知何故,它似乎没有遵循 VaryByParam 属性(或者说,我不确定自己到底是怎么回事):

public ActionResult View(UserViewCommand command) {

在这里,UserViewCommand有一个名为slug的属性,用于从数据库中查找用户。

这是我的OutputCache声明:

[HttpGet, OutputCache(Duration = 2000, VaryByParam = "None")]

然而,当我尝试使用不同的“slug”值(通过操作URL)来访问Action方法时,它不会提供错误的数据(这是我故意设计的),而是调用了Action方法。

例如(按调用顺序):

/user/view/abc -> 调用slug = abc的Action方法 /user/view/abc -> Action方法没有被调用 /user/view/xyz -> 再次调用slug = xyz的Action方法!难道它不应该因为VaryByParam = none而不从缓存中出来吗?

此外,在这种情况下,OutputCaching的推荐方式是什么?(如上所示的例子)

2个回答

11

5
VaryByParam只有在url的值看起来像/user/view?slug=abc时才起作用。参数必须是查询字符串参数,而不是像上面的例子中一样是url的一部分。原因很可能是因为缓存发生在任何url映射之前,而该映射不包含在缓存中。
更新
以下代码将帮助您实现目标。它没有考虑类似于Authorized过滤器之类的东西,但它将根据控制器/操作/ids进行缓存,但如果您设置ignore="slug",它将忽略该特定属性。
public class ActionOutputCacheAttribute : ActionFilterAttribute {
    public ActionOutputCacheAttribute(int cacheDuration, string ignore) {
        this.cacheDuration = cacheDuration;
        this.ignore = ignore;
    }

    private int cacheDuration;
    private string cacheKey;
    private string ignore;

    public override void OnActionExecuting(ActionExecutingContext filterContext) {
        string url = filterContext.HttpContext.Request.Url.PathAndQuery;
        this.cacheKey = ComputeCacheKey(filterContext);

        if (filterContext.HttpContext.Cache[this.cacheKey] != null) {
            //Setting the result prevents the action itself to be executed
            filterContext.Result =
            (ActionResult)filterContext.HttpContext.Cache[this.cacheKey];
        }

        base.OnActionExecuting(filterContext);
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext) {
        //Add the ActionResult to cache 
        filterContext.HttpContext.Cache.Add(this.cacheKey, filterContext.Result,null, DateTime.Now.AddSeconds(cacheDuration),
          System.Web.Caching.Cache.NoSlidingExpiration, CacheItemPriority.Normal, null);

        //Add a value in order to know the last time it was cached.
        filterContext.Controller.ViewData["CachedStamp"] = DateTime.Now;

        base.OnActionExecuted(filterContext);
    }

    private string ComputeCacheKey(ActionExecutingContext filterContext) {
        var keyBuilder = new StringBuilder();
        keyBuilder.Append(filterContext.ActionDescriptor.ControllerDescriptor.ControllerName);
        keyBuilder.Append(filterContext.ActionDescriptor.ActionName);

        foreach (var pair in filterContext.RouteData.Values) {
            if (pair.Key != ignore) 
                keyBuilder.AppendFormat("rd{0}_{1}_", pair.Key.GetHashCode(), pair.Value.GetHashCode());
        }
        return keyBuilder.ToString();
    }
}

但是我甚至没有在VaryByParam属性中指定'slug'参数。由于它实际上没有检查'slug',我不应该得到相同的结果吗?还是它也取决于请求的URL? - kidoman
这里的 None 只适用于 QueryString 值 - 整个 VaryByParam 选项只对 QueryStringValues 生效。我只是以你的 slug 作为例子 :) - Buildstarted
所以如果我理解正确的话,Cache是根据请求URL变化的?OutputCache(至少在MVC中实现的部分)似乎不支持这种情况,这很奇怪。还是我们有所遗漏? - kidoman
现在有一个不错的方法来做这件事...正在寻找它。 - Buildstarted
@BuildStarted: 当您计算密钥时,为什么没有使用ControllerName和ActionNames的HashCode而不是它们的名称? - Luciano
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接